# @stacksjs/env

> Stacks env helper methods.

Latest version **0.74.46** (published 2026-09-11) · MIT license · 0 weekly downloads

## Install

```sh
npm install @stacksjs/env
pnpm add @stacksjs/env
yarn add @stacksjs/env
bun add @stacksjs/env
```

## Health

**Score 65/100 (B)** — status: active.

Positive: esm support; no vulnerabilities; has provenance; recently updated; high maintenance score.

Warnings: low downloads; no types; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.74.46 |
| Published | 2026-09-11 |
| First published | 2024-01-31 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM |
| Dependencies | 1 |
| Unpacked size | 119.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 628 |
| Author | Chris Breuer |
| Maintainers | chrisbreuer, glenn123 |
| Keywords | env, utilities, functions, stacks |

## Links

- npm: https://www.npmjs.com/package/@stacksjs/env
- Repository: https://github.com/stacksjs/stacks
- Homepage: https://github.com/stacksjs/stacks/tree/main/storage/framework/core/env#readme
- Issues: https://github.com/stacksjs/stacks/issues
- Funding: https://github.com/sponsors/chrisbbreuer
- npm.io page: https://npm.io/package/@stacksjs/env

## Dependencies (1)

- [@stacksjs/path](https://npm.io/package/@stacksjs/path.md) 0.74.46

## Alternatives

- [replicas-cli](https://npm.io/package/replicas-cli.md) — 3.0K weekly downloads
- [env-contract](https://npm.io/package/env-contract.md) — 133 weekly downloads
- [@openveo/api](https://npm.io/package/@openveo/api.md) — 61 weekly downloads
- [@ryniaubenpm2/cumque-error-reiciendis](https://npm.io/package/@ryniaubenpm2/cumque-error-reiciendis.md) — 54 weekly downloads
- [ts-global-type-extra](https://npm.io/package/ts-global-type-extra.md) — 11 weekly downloads

## Recent versions

- 0.74.46 (latest) — 2026-09-11
- 0.74.45 — 2026-09-11
- 0.74.43 — 2026-09-11
- 0.74.42 — 2026-09-10
- 0.74.41 — 2026-09-10
- 0.74.40 — 2026-09-10
- 0.74.39 — 2026-09-10
- 0.74.38 — 2026-09-10
- 0.74.37 — 2026-09-10
- 0.74.36 — 2026-09-10
- 0.74.35 — 2026-09-10
- 0.74.34 — 2026-09-10
- 0.74.33 — 2026-09-09
- 0.74.32 — 2026-09-09
- 0.74.31 — 2026-09-08
- … 555 more at https://npm.io/package/@stacksjs/env/versions

## 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
- 🔑 **Versioned envelope** - X25519 + HKDF-SHA-256 + AES-256-GCM
- 📝 **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

```bash
bun add @stacksjs/env
```

## Quick Start

### 1. Auto-load .env files

```typescript
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)
```

When loading a named environment, `loadEnv` and `autoLoadEnv` look for its
`DOTENV_PRIVATE_KEY_<ENV>` key, then fall back to `DOTENV_PRIVATE_KEY` if the
scoped key is absent. This applies both to `.env.keys` (including a custom
`keysFile`) and to process environment variables. A standard single-key setup
does not need a duplicate `DOTENV_PRIVATE_KEY_DEVELOPMENT` alias.

The existing loader priority is unchanged: an explicit `privateKey`, then the
keys file, then process environment variables. Within each source the scoped
key wins over the generic key. A present key that fails decryption is an error,
not a reason to try a different key. Plaintext shell/CI value overrides are
still preserved unless `overload` is enabled.

### 2. Programmatic Usage

```typescript
import { loadEnv } from '@stacksjs/env'

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

### 3. Bun Plugin

Add to your `bunfig.toml`:

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

Or import in your preloader:

```typescript
import '@stacksjs/env/plugin'
```

## Encryption

### Encrypting .env Files

Use the buddy CLI to encrypt your environment variables:

```bash
# 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

```bash
# Decrypt .env file
buddy env:decrypt

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

### How Encryption Works

New writes use the experimental version 2 envelope proposed in
[`stacksjs/rfcs#6`](https://github.com/stacksjs/rfcs/issues/6):

1. A recipient keypair is generated using X25519.
2. Every value gets a fresh ephemeral X25519 pair, 16-byte HKDF salt, and
   12-byte AES-GCM nonce.
3. X25519 and HKDF-SHA-256 derive a one-use AES-256-GCM key.
4. Ciphertext and envelope metadata are authenticated; malformed, modified, and
   wrong-key inputs fail with the same non-secret error.

The pre-RFC `encrypted:<base64>` format can only be read for migration. New
writes are `encrypted:v2:<base64url>`, and legacy public keys are rejected. Run
`buddy env:rotate` to decrypt legacy values in memory, generate a version 2 key,
and replace the encrypted file without writing plaintext to disk.

This feature is not a complete secret-management system. It has not yet passed
the independent review required by [Stacks issue #2058](https://github.com/stacksjs/stacks/issues/2058),
so do not treat it as a production security boundary without your own review.
See [SECURITY.md](./SECURITY.md) for its threat model and non-guarantees.

**Example encrypted .env:**

```ini
# /-------------------[DOTENV_PUBLIC_KEY]--------------------/
# /            public-key encryption for .env files          /
# /       [how it works](https://stacksjs.com/encryption)   /
# /----------------------------------------------------------/
DOTENV_PUBLIC_KEY="x25519-public:<base64url-spki>"

# .env
API_KEY="encrypted:v2:<base64url-envelope>"
DB_PASSWORD="encrypted:v2:<base64url-envelope>"
```

## CLI Commands

All commands are available through the `buddy` CLI:

### Get Environment Variables

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

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

### Default Values

```ini
# 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

```ini
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

```ini
# 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:

```typescript
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:

```bash
# .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:

```ini
# .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.

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
import { decryptEnv } from '@stacksjs/env'

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

### `setEnv(key, value, options)`

Set an environment variable.

```typescript
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).

```typescript
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](https://github.com/stacksjs/stacks/releases) page for more information on what has changed recently.

## 🚜 Contributing

Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.

## 🏝 Community

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

[Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)

For casual chit-chat with others using this package:

[Join the Stacks Discord Server](https://stacksjs.com/discord)

## 📄 License

The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.

Made with 💙

---
_Source: https://npm.io/package/@stacksjs/env · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
