JSPM

  • Created
  • Published
  • Downloads 146
  • Score
    100M100P100Q73435F
  • License MIT

Stacks env helper methods.

Package Exports

  • @stacksjs/env
  • @stacksjs/env/index.js
  • @stacksjs/env/src/cli.d.ts
  • @stacksjs/env/src/crypto.d.ts
  • @stacksjs/env/src/index.d.ts
  • @stacksjs/env/src/parser.d.ts
  • @stacksjs/env/src/plugin.d.ts
  • @stacksjs/env/src/types.d.ts
  • @stacksjs/env/src/utils.d.ts

Readme

@stacksjs/env

A secure .env file management package with built-in encryption support for Bun and Node.js.

Features

  • 🔐 Automatic Encryption/Decryption - Secure your environment variables with public-key cryptography
  • 🚀 Bun Plugin - Seamless integration with Bun's runtime
  • 🔑 secp256k1 ECIES - Industry-standard elliptic curve encryption
  • 📝 Variable Expansion - Support for ${VAR}, defaults, and alternates
  • 🔧 Command Substitution - Execute commands with $(command)
  • 🎯 Multi-Environment - Manage multiple .env files for different environments
  • 🛠️ CLI Tools - Full-featured CLI via buddy commands
  • 🌍 Environment Detection - Native runtime, platform, and CI/CD detection utilities

Installation

bun add @stacksjs/env

Quick Start

1. Auto-load .env files

import { autoLoadEnv } from '@stacksjs/env'

// Automatically loads .env files based on NODE_ENV or DOTENV_ENV
autoLoadEnv()

// Now use your environment variables
console.log(process.env.MY_SECRET)

2. Programmatic Usage

import { loadEnv } from '@stacksjs/env'

// Load specific .env files
loadEnv({
  path: ['.env.local', '.env'],
  overload: false,
})

3. Bun Plugin

Add to your bunfig.toml:

preload = ["./storage/framework/core/env/plugin.ts"]

Or import in your preloader:

import '@stacksjs/env/plugin'

Encryption

Encrypting .env Files

Use the buddy CLI to encrypt your environment variables:

# Encrypt .env file
buddy env:encrypt

# Encrypt specific file
buddy env:encrypt --file .env.production

# Encrypt specific keys only
buddy env:encrypt -k "SECRET**"

# Exclude specific keys from encryption
buddy env:encrypt -ek "PUBLIC**"

This will:

  1. Generate a public/private keypair
  2. Store keys in .env.keys (keep this secure!)
  3. Encrypt values in your .env file
  4. Add DOTENV_PUBLIC_KEY to your .env file

Decrypting .env Files

# Decrypt .env file
buddy env:decrypt

# Decrypt specific file
buddy env:decrypt --file .env.production

How Encryption Works

The encryption uses secp256k1 ECIES (Elliptic Curve Integrated Encryption Scheme):

  1. A keypair is generated using secp256k1 (same as Bitcoin)
  2. Each value is encrypted with AES-256-GCM using an ephemeral key
  3. The ephemeral key is encrypted with the public key
  4. Only the private key can decrypt the values

Example encrypted .env:

# /-------------------[DOTENV_PUBLIC_KEY]--------------------/
# /            public-key encryption for .env files          /
# /       [how it works](https://stacksjs.com/encryption)   /
# /----------------------------------------------------------/
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"

# .env
API_KEY="encrypted:BDqDBibm4wsYqMpCjTQ6BsDHmMadg9K3dAt+Z9HPMfLEIRVz50hmLXPXRuDBXaJi..."
DB_PASSWORD="encrypted:AKx8Bh3m5xtZrNqDkUP7CuEInOcfg9L4eBy/2qt59vbSU0aN9WSmN..."

CLI Commands

All commands are available through the buddy CLI:

Get Environment Variables

# Get a specific variable
buddy env:get API_KEY

# Get all variables as JSON
buddy env:get --all

# Get all variables in shell format
buddy env:get --all --format shell

# Pretty print JSON
buddy env:get --all --pretty

Set Environment Variables

# Set a variable (encrypted by default)
buddy env:set API_KEY "my-secret-value"

# Set without encryption
buddy env:set PUBLIC_URL "https://example.com" --plain

# Set in specific file
buddy env:set API_KEY "value" --file .env.production

Manage Keypairs

# View keypair
buddy env:keypair

# View keypair for specific environment
buddy env:keypair --file .env.production

# Get specific key
buddy env:keypair DOTENV_PRIVATE_KEY

# Output in shell format
buddy env:keypair --format shell

Rotate Keys

# Rotate keypair and re-encrypt all values
buddy env:rotate

# Rotate for specific environment
buddy env:rotate --file .env.production

Variable Expansion

The env parser supports advanced variable expansion:

Basic Expansion

USERNAME="john"
DATABASE_URL="postgres://${USERNAME}@localhost/mydb"
# Result: postgres://john@localhost/mydb

Default Values

# Use default if unset or empty
DATABASE_HOST=${DB_HOST:-localhost}
DATABASE_PORT=${DB_PORT:-5432}

# Use default only if unset (empty is ok)
API_URL=${API_BASE_URL-https://api.example.com}

Alternate Values

NODE_ENV=production

# Use alternate if set and non-empty
DEBUG_MODE=${NODE_ENV:+false}
LOG_LEVEL=${NODE_ENV:+error}

# Use alternate if set (empty is ok)
CACHE_ENABLED=${NODE_ENV+true}

Command Substitution

# Execute command and use output
CURRENT_USER=$(whoami)
BUILD_TIME=$(date +%s)
GIT_COMMIT=$(git rev-parse HEAD)

Environment Detection

The package includes native utilities to detect runtime, platform, and CI/CD environments:

import {
  // Runtime detection
  isBun,
  isNode,
  runtime,
  runtimeInfo,

  // Platform detection
  platform,
  isWindows,
  isMacOS,
  isLinux,

  // Environment detection
  hasTTY,
  hasWindow,
  isCI,
  isDebug,
  isMinimal,
  isColorSupported,

  // Provider detection
  provider,
  providerInfo,
} from '@stacksjs/env'

// Check runtime
console.log(runtime) // 'bun' | 'node' | 'unknown'
console.log(runtimeInfo) // { name: 'bun', version: '1.3.2' }

// Check platform
console.log(platform) // 'darwin' | 'linux' | 'win32' | etc.
console.log(isMacOS) // true/false

// Check CI environment
console.log(isCI) // true/false
console.log(provider) // 'github' | 'gitlab' | 'vercel' | etc.
console.log(providerInfo) // { name: 'GitHub Actions', detected: true }

Supported CI/CD Providers

  • GitHub Actions
  • GitLab CI
  • CircleCI
  • Travis CI
  • Jenkins
  • Vercel
  • Netlify
  • Heroku
  • AWS
  • Azure
  • Cloudflare Pages
  • Railway
  • Render

Multi-Environment Support

Load different .env files based on environment:

# .env.local (highest priority)
# .env.development
# .env.production
# .env (lowest priority)

The loader will automatically detect NODE_ENV or DOTENV_ENV and load the appropriate files.

Environment-Specific Keys

Keys are automatically namespaced by environment:

# .env.keys
DOTENV_PUBLIC_KEY="..."
DOTENV_PRIVATE_KEY="..."

DOTENV_PUBLIC_KEY_PRODUCTION="..."
DOTENV_PRIVATE_KEY_PRODUCTION="..."

DOTENV_PUBLIC_KEY_CI="..."
DOTENV_PRIVATE_KEY_CI="..."

Security Best Practices

  1. Never commit .env.keys - Add to .gitignore
  2. Commit encrypted .env files - They're safe to commit
  3. Store private keys securely - Use your CI/CD secrets manager
  4. Rotate keys regularly - Use buddy env:rotate
  5. Use environment-specific keys - Different keys for dev/staging/prod

API Reference

autoLoadEnv(options?)

Automatically load .env files based on environment.

import { autoLoadEnv } from '@stacksjs/env'

autoLoadEnv({
  env: 'production',  // Override environment detection
  overload: false,    // Don't override existing vars
  quiet: true,        // Suppress output
  cwd: '/path/to/project'
})

loadEnv(options)

Load specific .env files.

import { loadEnv } from '@stacksjs/env'

loadEnv({
  path: ['.env.local', '.env'],
  overload: false,
  privateKey: 'your-private-key',
  keysFile: '.env.keys',
  quiet: false
})

encryptEnv(options)

Encrypt a .env file.

import { encryptEnv } from '@stacksjs/env'

const result = encryptEnv({
  file: '.env',
  keysFile: '.env.keys',
  key: 'SECRET**',        // Only encrypt keys matching pattern
  excludeKey: 'PUBLIC**',  // Exclude keys matching pattern
  stdout: false
})

decryptEnv(options)

Decrypt a .env file.

import { decryptEnv } from '@stacksjs/env'

const result = decryptEnv({
  file: '.env',
  keysFile: '.env.keys',
  stdout: false
})

setEnv(key, value, options)

Set an environment variable.

import { setEnv } from '@stacksjs/env'

setEnv('API_KEY', 'my-secret', {
  file: '.env',
  keysFile: '.env.keys',
  plain: false  // Encrypt by default
})

getEnv(key?, options)

Get environment variable(s).

import { getEnv } from '@stacksjs/env'

// Get single value
const result = getEnv('API_KEY', {
  file: '.env',
  keysFile: '.env.keys'
})

// Get all values
const result = getEnv(undefined, {
  all: true,
  format: 'json',  // or 'shell' or 'eval'
  prettyPrint: true
})

Migration from dotenvx

This package replaces @dotenvx/dotenvx and bun-plugin-dotenvx with a native Bun implementation.

Breaking Changes

None! The API is designed to be compatible with dotenvx.

Migration Steps

  1. Update your bunfig.toml preload
  2. Update imports from @dotenvx/dotenvx to @stacksjs/env
  3. buddy commands remain the same

📈 Changelog

Please see our releases page for more information on what has changed recently.

🚜 Contributing

Please review the Contributing Guide for details.

🏝 Community

For help, discussion about best practices, or any other conversation that would benefit from being searchable:

Discussions on GitHub

For casual chit-chat with others using this package:

Join the Stacks Discord Server

📄 License

The MIT License (MIT). Please see LICENSE for more information.

Made with 💙