environment-setup · diff
git:20260306.b04d94a to v1.1
154 added, 315 removed. Audit A to A.
---
name: environment-setup
- description: Configure and manage development, staging, and production environments. Use when setting up environment variables, managing configurations, or separating environments. Handles .env files, config management, and environment-specific settings.
- allowed-tools: Read Write Edit Bash
+ description: >
+ Organize application environment configuration: `.env` files, env precedence,
+ typed env validation, secret handoff, framework-specific env rules, and config
+ drift between local, staging, CI, and production. Use when the user needs help
+ structuring environment variables, validating required config, separating
+ public/private env values, or cleaning up env-file sprawl. This is the narrower
+ app-config compatibility skill. Route broader runnable-machine, Docker,
+ devcontainer, onboarding, and local-service setup work to `system-environment-setup`.
+ allowed-tools: Bash Read Write Edit Glob Grep
+ compatibility: >
+ Best when the main problem is application configuration rather than whole-machine
+ reproducibility: env files, config layering, schema validation, and secrets
+ handoff boundaries.
metadata:
- tags: environment, configuration, env-variables, dotenv, config-management
- platforms: Claude, ChatGPT, Gemini
+ tags: environment, env-files, dotenv, env-validation, config-management, secrets-handoff
+ platforms: Claude, ChatGPT, Gemini, Codex
+ version: "1.1"
+ source: akillness/oh-my-skills
---
-
# Environment Configuration
-
- ## When to use this skill
-
- - **New Projects**: Initial environment setup
- - **Multiple Environments**: Separate dev, staging, production
- - **Team Collaboration**: Share consistent environments
-
- ## Instructions
-
- ### Step 1: .env File Structure
-
- **.env.example** (template):
- ```bash
- # Application
- NODE_ENV=development
- PORT=3000
- APP_URL=http://localhost:3000
-
- # Database
- DATABASE_URL=postgresql://user:password@localhost:5432/myapp
- DATABASE_POOL_MIN=2
- DATABASE_POOL_MAX=10
-
- # Redis
- REDIS_URL=redis://localhost:6379
- REDIS_TTL=3600
-
- # Authentication
- JWT_ACCESS_SECRET=change-me-in-production-min-32-characters
- JWT_REFRESH_SECRET=change-me-in-production-min-32-characters
- JWT_ACCESS_EXPIRY=15m
- JWT_REFRESH_EXPIRY=7d
-
- # Email
- SMTP_HOST=smtp.gmail.com
- SMTP_PORT=587
- SMTP_USER=your-email@gmail.com
- SMTP_PASSWORD=your-app-password
-
- # External APIs
- STRIPE_SECRET_KEY=sk_test_xxx
- STRIPE_PUBLISHABLE_KEY=pk_test_xxx
- AWS_ACCESS_KEY_ID=AKIAXXXXXXXX
- AWS_SECRET_ACCESS_KEY=xxxxxxxx
- AWS_REGION=us-east-1
- AWS_S3_BUCKET=myapp-uploads
-
- # Monitoring
- SENTRY_DSN=https://xxx@sentry.io/xxx
- LOG_LEVEL=info
-
- # Feature Flags
- ENABLE_2FA=false
- ENABLE_ANALYTICS=true
- ```
-
- **.env.local** (per developer):
- ```bash
- # Developer personal settings (add to .gitignore)
- DATABASE_URL=postgresql://localhost:5432/myapp_dev
- LOG_LEVEL=debug
- ```
-
- **.env.production**:
- ```bash
- NODE_ENV=production
- PORT=8080
- APP_URL=https://myapp.com
-
- DATABASE_URL=${DATABASE_URL} # Injected from environment variables
- REDIS_URL=${REDIS_URL}
-
- JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
-
- LOG_LEVEL=warn
- ENABLE_2FA=true
- ```
-
- ### Step 2: Type-Safe Environment Variables (TypeScript)
-
- **config/env.ts**:
- ```typescript
- import { z } from 'zod';
- import dotenv from 'dotenv';
-
- // Load .env file
- dotenv.config();
-
- // Define schema
- const envSchema = z.object({
- NODE_ENV: z.enum(['development', 'production', 'test']),
- PORT: z.coerce.number().default(3000),
+ Use this skill as the repository's **narrower application-config and `.env` compatibility skill**.
- DATABASE_URL: z.string().url(),
+ The job is to make app configuration clear and safe across environments:
+ - decide what belongs in env vars,
+ - structure `.env` files and precedence rules,
+ - validate required config,
+ - separate public/private values,
+ - reduce config drift between local, CI, staging, and production,
+ - make secret handoff explicit.
- JWT_ACCESS_SECRET: z.string().min(32),
- JWT_REFRESH_SECRET: z.string().min(32),
+ Read [references/env-patterns.md](references/env-patterns.md) and [references/scope-boundaries.md](references/scope-boundaries.md) before unusual cases or when deciding whether the real need belongs in `system-environment-setup`.
- SMTP_HOST: z.string(),
- SMTP_PORT: z.coerce.number(),
- SMTP_USER: z.string().email(),
- SMTP_PASSWORD: z.string(),
+ If the user mainly needs:
+ - **toolchains, local services, Docker Compose, devcontainers, or onboarding** → route to `system-environment-setup`
+ - **deployment or CI secret wiring** → pair with `deployment-automation`
+ - **security architecture or policy** → pair with `security-best-practices`
- STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
+ ## When to use this skill
+ - Design `.env.example`, `.env.local`, or per-environment config structure
+ - Explain env precedence and which values should or should not be committed
+ - Validate required env vars with typed/runtime checks
+ - Separate server-only vs client-exposed env values
+ - Reduce drift between local, CI, staging, and production config
+ - Clean up a repo where env files, secret docs, and runtime expectations disagree
+ - Decide when env vars are enough and when secret-manager injection is needed
- LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
- });
+ ## When not to use this skill
+ - The main task is making the full repo runnable across machines → use `system-environment-setup`
+ - The main task is local service orchestration, Docker, or devcontainers → use `system-environment-setup`
+ - The main task is deployment automation or production rollout → use `deployment-automation`
+ - The main task is broader security review rather than config organization → pair with `security-best-practices`
- // Validate and export
- export const env = envSchema.parse(process.env);
+ ## Instructions
- // Usage:
- // import { env } from './config/env';
- // console.log(env.DATABASE_URL); // Type-safe!
- ```
+ ### Step 1: Classify the config problem
+ Normalize the request into this intake first:
- **Error Handling**:
- ```typescript
- try {
- const env = envSchema.parse(process.env);
- } catch (error) {
- if (error instanceof z.ZodError) {
- console.error('❌ Invalid environment variables:');
- error.errors.forEach((err) => {
- console.error(` - ${err.path.join('.')}: ${err.message}`);
- });
- process.exit(1);
- }
- }
+ ```yaml
+ env_config_intake:
+ primary_goal: env-structure | validation | secret-handoff | public-private-split | drift-cleanup | framework-rules | unknown
+ app_shape: backend | frontend | fullstack | monorepo | unknown
+ current_storage: env-files | framework-config | secret-manager | mixed | unknown
+ drift_surface:
+ - missing-required-vars
+ - duplicate-env-files
+ - CI-local-mismatch
+ - public-private-leak-risk
+ - undocumented-secret-source
+ - framework-prefix-confusion
+ - unclear
+ confidence: high | medium | low
```
- ### Step 3: Per-Environment Config Files
-
- **config/index.ts**:
- ```typescript
- interface Config {
- env: string;
- port: number;
- database: {
- url: string;
- pool: { min: number; max: number };
- };
- jwt: {
- accessSecret: string;
- refreshSecret: string;
- accessExpiry: string;
- refreshExpiry: string;
- };
- features: {
- enable2FA: boolean;
- enableAnalytics: boolean;
- };
- }
-
- const config: Config = {
- env: process.env.NODE_ENV || 'development',
- port: parseInt(process.env.PORT || '3000'),
-
- database: {
- url: process.env.DATABASE_URL!,
- pool: {
- min: parseInt(process.env.DATABASE_POOL_MIN || '2'),
- max: parseInt(process.env.DATABASE_POOL_MAX || '10'),
- },
- },
+ ### Step 2: Choose one primary mode
+ Pick exactly one mode for the run:
- jwt: {
- accessSecret: process.env.JWT_ACCESS_SECRET!,
- refreshSecret: process.env.JWT_REFRESH_SECRET!,
- accessExpiry: process.env.JWT_ACCESS_EXPIRY || '15m',
- refreshExpiry: process.env.JWT_REFRESH_EXPIRY || '7d',
- },
+ 1. **env-file-structure**
+ - Use when the main need is file layout, naming, and precedence.
+ 2. **env-validation**
+ - Use when missing or malformed values are causing runtime/build pain.
+ 3. **secret-handoff-boundary**
+ - Use when `.env` files are colliding with secret-manager or credential-delivery concerns.
+ 4. **framework-config-rules**
+ - Use when the main problem is framework-specific env behavior (public/private prefixes, build-time vs runtime exposure, etc.).
+ 5. **drift-cleanup**
+ - Use when env templates, docs, CI vars, and actual runtime expectations disagree.
- features: {
- enable2FA: process.env.ENABLE_2FA === 'true',
- enableAnalytics: process.env.ENABLE_ANALYTICS !== 'false',
- },
- };
+ ### Step 3: Apply config rules
+ - Keep deploy-specific values out of source code and commit only safe templates.
+ - Separate **committed templates** from **developer-local values**.
+ - Make public/client-exposed env vars visually distinct from server-only values.
+ - Prefer typed validation when the app is large enough for env drift to be expensive.
+ - Record the source of secrets: local file, secret manager, CI variable, or cloud platform.
+ - Route outward when the actual blocker is machine setup, Docker, or local services rather than config design.
- // Validate required fields
- const requiredEnvVars = [
- 'DATABASE_URL',
- 'JWT_ACCESS_SECRET',
- 'JWT_REFRESH_SECRET',
- ];
+ ### Step 4: Build the config brief
+ Return this exact structure:
- for (const envVar of requiredEnvVars) {
- if (!process.env[envVar]) {
- throw new Error(`Missing required environment variable: ${envVar}`);
- }
- }
+ ```markdown
+ # Environment Config Brief
- export default config;
- ```
+ ## Recommended mode
+ - Mode: env-file-structure | env-validation | secret-handoff-boundary | framework-config-rules | drift-cleanup
+ - Why this mode fits: ...
- ### Step 4: Environment-Specific Configuration Files
+ ## Current config surface
+ - App shape: ...
+ - Config sources: ...
+ - Main drift or risk: ...
+ - Confidence: high | medium | low
- **config/environments/development.ts**:
- ```typescript
- export default {
- logging: {
- level: 'debug',
- prettyPrint: true,
- },
- cors: {
- origin: '*',
- credentials: true,
- },
- rateLimit: {
- enabled: false,
- },
- };
- ```
+ ## Recommended config layout
+ 1. ...
+ 2. ...
+ 3. ...
- **config/environments/production.ts**:
- ```typescript
- export default {
- logging: {
- level: 'warn',
- prettyPrint: false,
- },
- cors: {
- origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
- credentials: true,
- },
- rateLimit: {
- enabled: true,
- windowMs: 15 * 60 * 1000,
- max: 100,
- },
- };
+ ## Example files / checks
+ ```bash
+ ...
```
- **config/index.ts** (unified):
- ```typescript
- import development from './environments/development';
- import production from './environments/production';
-
- const env = process.env.NODE_ENV || 'development';
+ ## Why this layout is safer
+ - ...
+ - ...
- const configs = {
- development,
- production,
- test: development,
- };
+ ## Watch-outs
+ - ...
+ - ...
- export const environmentConfig = configs[env];
+ ## Adjacent handoff
+ - Use `system-environment-setup` for ...
+ - Use `deployment-automation` for ...
+ - Use `security-best-practices` for ...
```
- ### Step 5: Docker Environment Variables
-
- **docker-compose.yml**:
- ```yaml
- version: '3.8'
-
- services:
- app:
- build: .
- environment:
- - NODE_ENV=development
- - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- - REDIS_URL=redis://redis:6379
- env_file:
- - .env.local
- depends_on:
- - db
- - redis
+ ### Step 5: Use mode-specific guidance
- db:
- image: postgres:15-alpine
- environment:
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: password
- POSTGRES_DB: myapp
+ **For env-file-structure**
+ - Define the committed template files first.
+ - Call out local-only overrides and ignored files explicitly.
+ - Make precedence easy to explain.
- redis:
- image: redis:7-alpine
- ```
+ **For env-validation**
+ - Choose a schema/validation layer appropriate for the stack.
+ - Fail fast with clear missing-variable messages.
+ - Keep validation close to app startup.
- ## Output format
+ **For secret-handoff-boundary**
+ - Name which values are safe in templates and which must come from a secret source.
+ - Document how a developer obtains sensitive values.
+ - Avoid pretending secret-manager adoption removes the need for local conventions.
- ```
- project/
- ├── .env.example # Template (commit)
- ├── .env # Local (gitignore)
- ├── .env.local # Per developer (gitignore)
- ├── .env.production # Production (gitignore or vault)
- ├── config/
- │ ├── index.ts # Main configuration
- │ ├── env.ts # Environment variable validation
- │ └── environments/
- │ ├── development.ts
- │ ├── production.ts
- │ └── test.ts
- └── .gitignore
- ```
+ **For framework-config-rules**
+ - Explain public/private env prefixes and build-time/runtime behavior.
+ - Call out framework-specific exposure risks.
- **.gitignore**:
- ```
- .env
- .env.local
- .env.*.local
- .env.production
- ```
+ **For drift-cleanup**
+ - Compare templates, runtime code, CI vars, and docs.
+ - Remove duplicate or stale env file conventions.
+ - Make one source of truth obvious.
- ## Constraints
+ ### Step 6: Keep boundaries sharp
+ Before finalizing:
+ - Do **not** turn this into a Docker/devcontainer tutorial.
+ - Do **not** bury public/private env exposure risk.
+ - Do **not** assume `.env` files are enough for all secret workflows.
+ - Do **not** keep this as a peer duplicate of `system-environment-setup`; route broader setup work outward.
- ### Required Rules (MUST)
+ ## Examples
- 1. **Provide .env.example**: List of required environment variables
- 2. **Validation**: Error when required environment variables are missing
- 3. **.gitignore**: Never commit .env files
+ ### Example 1: Env template cleanup
+ Input: "Help me structure `.env.example` and `.env.local` so new devs stop guessing values."
+ Output: chooses `env-file-structure`, defines committed templates vs local overrides, and keeps the scope at app config.
- ### Prohibited (MUST NOT)
+ ### Example 2: Validation hardening
+ Input: "We keep forgetting env vars until runtime."
+ Output: chooses `env-validation`, recommends typed validation, and shows how to fail fast.
- 1. **Commit Secrets**: Never commit .env files
- 2. **Hardcoding**: Do not hardcode environment-specific settings in code
+ ### Example 3: Framework split
+ Input: "Which env vars can be exposed to the frontend and which must stay server-only?"
+ Output: chooses `framework-config-rules`, explains the client/server split, and avoids broad setup drift.
## Best practices
-
- 1. **12 Factor App**: Manage configuration via environment variables
- 2. **Type Safety**: Runtime validation with Zod
- 3. **Secrets Management**: Use AWS Secrets Manager, Vault
+ 1. Treat this as the app-config layer, not the whole-machine setup skill.
+ 2. Make secret sources explicit rather than implied.
+ 3. Prefer committed templates plus local-only overrides.
+ 4. Add validation once env drift becomes expensive.
+ 5. Route broader runnable-repo work to `system-environment-setup`.
## References
-
- - [dotenv](https://github.com/motdotla/dotenv)
- - [Zod](https://zod.dev/)
- - [12 Factor App - Config](https://12factor.net/config)
-
- ## Metadata
-
- ### Version
- - **Current Version**: 1.0.0
- - **Last Updated**: 2025-01-01
- - **Compatible Platforms**: Claude, ChatGPT, Gemini
-
- ### Tags
- `#environment` `#configuration` `#env-variables` `#dotenv` `#config-management` `#utilities`
-
- ## Examples
-
- ### Example 1: Basic usage
- <!-- Add example content here -->
-
- ### Example 2: Advanced usage
- <!-- Add advanced example content here -->
+ - [Environment patterns](references/env-patterns.md)
+ - [Scope boundaries](references/scope-boundaries.md)
+ - [Twelve-Factor config](https://12factor.net/config)
+ - [Next.js env guide](https://nextjs.org/docs/app/guides/environment-variables)
+ - [T3 Env intro](https://env.t3.gg/docs/introduction)