| name | stacks-env |
| description | Use when working with environment variables in Stacks — the typed env proxy with auto-coercion, .env file loading, AES-256-GCM encryption/decryption of env values, runtime/platform detection, CI provider detection, or the env CLI commands. Covers @stacksjs/env, config/env.ts, and .env files. |
| license | MIT |
| compatibility | Bun >= 1.3.0, TypeScript |
| allowed-tools | Read Edit Write Bash Grep Glob |
Stacks Environment
Key Paths
- Core package:
storage/framework/core/env/src/
- Environment config:
config/env.ts
- Environment file:
.env
- Example:
.env.example
- Type definitions:
storage/framework/env.d.ts
Source Files
env/src/
├── index.ts # main exports
├── utils.ts # runtime/platform detection, StacksEnv proxy
├── plugin.ts # Bun plugin for auto .env loading
├── parser.ts # .env file parser with encryption support
├── cli.ts # CLI commands (get, set, encrypt, decrypt, rotate)
├── crypto.ts # AES-256-GCM + secp256k1 encryption
└── types.ts # StacksEnv interface (100+ typed vars)
Typed Environment Proxy
import { env } from '@stacksjs/env'
env.APP_NAME
env.APP_ENV
env.APP_KEY
env.APP_URL
env.DEBUG
env.PORT
env.DB_CONNECTION
env.DB_HOST
env.DB_PORT
env.DB_DATABASE
env.DB_USERNAME
env.DB_PASSWORD
env.AWS_ACCESS_KEY_ID
env.STRIPE_SECRET_KEY
env.MAIL_MAILER
env.QUEUE_DRIVER
env.REDIS_HOST
env[key]
The env proxy auto-coerces: 'true' → true, '123' → 123, etc.
StacksEnv Type (100+ typed variables)
App, Ports, API, Database, AWS, Mail, Services (Stripe, Meilisearch), Frontend, Realtime, Redis, Pusher, Auth, Storage, Queue, plus [key: string] catch-all.
Runtime Detection
import { isBun, isNode, runtime, runtimeInfo, platform, isWindows, isMacOS, isLinux, hasTTY, hasWindow, isCI, isDebug, isMinimal, isColorSupported, provider, providerInfo } from '@stacksjs/env'
isBun
isNode
runtime
platform
isWindows
isMacOS
isLinux
hasTTY
isCI
isDebug
isColorSupported
CI Provider Detection
provider
providerInfo
.env File Loading
import { loadEnv, autoLoadEnv, envPlugin } from '@stacksjs/env'
await loadEnv({ path: '.env', override: false })
autoLoadEnv()
envPlugin()
.env Parser
import { parse } from '@stacksjs/env'
const vars = parse(envContent, {
encryption: true,
privateKey: '...'
})
Encryption (AES-256-GCM)
import { aesEncrypt, aesDecrypt, generateKeypair, encryptValue, decryptValue, getPrivateKey } from '@stacksjs/env'
const encrypted = aesEncrypt(plaintext, password)
const decrypted = aesDecrypt(encrypted, password)
const keypair = generateKeypair()
const encrypted = encryptValue(value, keypair.publicKey)
const decrypted = decryptValue(encrypted, keypair.privateKey)
CLI Commands
buddy env:get APP_NAME
buddy env:get --all --format json
buddy env:set APP_NAME "My App"
buddy env:encrypt
buddy env:decrypt
buddy env:keypair
buddy env:rotate
buddy env:check
buddy env:check --file .env.production
Tenant isolation on a shared box
When several projects share one server - one owns it, the rest attach with
cloud.attachTo - each still deploys from its own repository with its own
.env.<environment>. No project needs another's values.
They leak anyway: a tenant's secrets get pasted into the owner's env file under
a TENANT_ prefix while debugging a deploy, and stay. That is not just untidy.
buddy deploy ships the whole env file as every site's .env (ts-cloud
treats site.env as the complete file), so a stray BUGHQ_STRIPE_SECRET_KEY in
the owner's file lands on disk in an unrelated site.
Declare who is attached, in config/cloud.ts's default export:
const config: CloudConfig = {
tenants: ['bughq', 'analyticshq'],
}
With that:
buddy deploy drops those keys before shipping, and logs what it dropped
buddy env:check lists them per tenant so they can be deleted at source
Prefixes are never inferred. With no tenants declared nothing is treated
as foreign, because STRIPE_, AWS_ and MEILISEARCH_ are indistinguishable
from a slug prefix by shape alone. Slug punctuation is ignored, so
analytics-hq and analytics_hq both match ANALYTICSHQ_.
The API is partitionTenantEnv(values, { self, tenants }) from
@stacksjs/env, plus stripForeignTenantEnv and foreignTenantKeys.
Gotchas
- A tenant's keys in your env file get shipped everywhere.
buddy deploy
sends the entire env file as each site's .env. Declare tenants in
config/cloud.ts so they are stripped, then delete them at source
- Bun natively loads
.env — no dotenv package needed
- The
env proxy auto-coerces strings to booleans/numbers
.env should never be committed — use .env.example as template
- Encryption uses AES-256-GCM for values and secp256k1 for key exchange
autoLoadEnv() loads in order: .env, .env.local, .env.{APP_ENV}
- Runtime detection uses Bun globals and process properties
- CI provider detection checks environment variables specific to each CI system
- The
StacksEnv type provides autocomplete for 100+ known variables