config-manager

Configuration Manager Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "config-manager" with this command: npx skills add eddiebe147/claude-settings/eddiebe147-claude-settings-config-manager

Configuration Manager Skill

Overview

This skill helps you design and implement robust configuration management for applications. Covers environment variables, config files, secrets management, validation, type safety, and multi-environment deployments.

Configuration Philosophy

Twelve-Factor App Principles

  • Store config in the environment: Separate config from code

  • Strict separation: Config that varies between deploys should be in env vars

  • No secrets in code: Ever. Period.

Configuration Hierarchy

Priority (highest to lowest):

  1. Command-line arguments
  2. Environment variables
  3. Environment-specific config files (.env.local)
  4. Default config files (.env)
  5. Application defaults in code

What Goes Where

  • Environment Variables: API keys, database URLs, feature flags

  • Config Files: Non-sensitive defaults, complex structures

  • Secrets Manager: Production credentials, API tokens

  • Code: Application logic, never secrets

Environment Variables

.env File Structure

.env.example - Commit this file as documentation

Copy to .env and fill in values

===================

Application

===================

NODE_ENV=development PORT=3000 APP_URL=http://localhost:3000

===================

Database

===================

DATABASE_URL=postgresql://user:password@localhost:5432/mydb DATABASE_POOL_SIZE=10

===================

Authentication

===================

Get from Supabase Dashboard > Settings > API

NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

===================

External Services

===================

STRIPE_SECRET_KEY=sk_test_xxx STRIPE_WEBHOOK_SECRET=whsec_xxx NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx

===================

Email

===================

RESEND_API_KEY=re_xxx EMAIL_FROM=noreply@example.com

===================

Feature Flags

===================

ENABLE_ANALYTICS=true ENABLE_BETA_FEATURES=false

.gitignore Configuration

Environment files

.env .env.local .env.*.local .env.development.local .env.test.local .env.production.local

Keep example file

!.env.example

Multi-Environment Setup

.env.development

NODE_ENV=development DATABASE_URL=postgresql://localhost:5432/myapp_dev LOG_LEVEL=debug

.env.test

NODE_ENV=test DATABASE_URL=postgresql://localhost:5432/myapp_test LOG_LEVEL=error

.env.production

NODE_ENV=production DATABASE_URL=${DATABASE_URL} # Set in deployment platform LOG_LEVEL=info

Type-Safe Configuration

Zod Schema Validation

// src/config/env.ts import { z } from 'zod';

// Define schema const envSchema = z.object({ // Node environment NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),

// Server PORT: z.coerce.number().default(3000), APP_URL: z.string().url(),

// Database DATABASE_URL: z.string().url(), DATABASE_POOL_SIZE: z.coerce.number().min(1).max(100).default(10),

// Supabase NEXT_PUBLIC_SUPABASE_URL: z.string().url(), NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1), SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),

// Stripe (optional in development) STRIPE_SECRET_KEY: z.string().startsWith('sk_').optional(), STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_').optional(),

// Feature flags ENABLE_ANALYTICS: z.coerce.boolean().default(false), ENABLE_BETA_FEATURES: z.coerce.boolean().default(false), });

// Type export export type Env = z.infer<typeof envSchema>;

// Parse and validate function loadEnv(): Env { const result = envSchema.safeParse(process.env);

if (!result.success) { console.error('Invalid environment variables:'); console.error(result.error.format()); process.exit(1); }

return result.data; }

// Singleton export export const env = loadEnv();

Environment Validation at Build Time

// src/config/validate-env.ts import { z } from 'zod';

// Client-side env (exposed to browser) const clientEnvSchema = z.object({ NEXT_PUBLIC_SUPABASE_URL: z.string().url(), NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1), NEXT_PUBLIC_APP_URL: z.string().url(), });

// Server-side env (never exposed to browser) const serverEnvSchema = z.object({ DATABASE_URL: z.string(), SUPABASE_SERVICE_ROLE_KEY: z.string(), STRIPE_SECRET_KEY: z.string().optional(), });

// Combined const envSchema = clientEnvSchema.merge(serverEnvSchema);

export function validateEnv() { // Only validate server-side env on server if (typeof window !== 'undefined') { return clientEnvSchema.parse({ NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, }); }

return envSchema.parse(process.env); }

T3 Env Pattern (Next.js)

// src/env.mjs import { createEnv } from '@t3-oss/env-nextjs'; import { z } from 'zod';

export const env = createEnv({ /**

  • Server-side environment variables */ server: { DATABASE_URL: z.string().url(), NODE_ENV: z.enum(['development', 'test', 'production']), SUPABASE_SERVICE_ROLE_KEY: z.string(), STRIPE_SECRET_KEY: z.string().startsWith('sk_'), },

/**

  • Client-side environment variables (exposed to browser)
  • Prefix with NEXT_PUBLIC_ */ client: { NEXT_PUBLIC_SUPABASE_URL: z.string().url(), NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(), NEXT_PUBLIC_APP_URL: z.string().url(), },

/**

  • Runtime values */ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY, STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY, NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL, },

/**

  • Skip validation in certain environments */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, });

Configuration Files

JSON Configuration

// config/default.json { "app": { "name": "My Application", "version": "1.0.0" }, "server": { "port": 3000, "host": "localhost" }, "cache": { "ttl": 3600, "maxSize": 1000 }, "features": { "darkMode": true, "betaFeatures": false } }

// config/production.json (overrides default) { "server": { "host": "0.0.0.0" }, "cache": { "ttl": 86400 } }

Config Loader

// src/config/loader.ts import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; import { z } from 'zod';

const configSchema = z.object({ app: z.object({ name: z.string(), version: z.string(), }), server: z.object({ port: z.number(), host: z.string(), }), cache: z.object({ ttl: z.number(), maxSize: z.number(), }), features: z.object({ darkMode: z.boolean(), betaFeatures: z.boolean(), }), });

type Config = z.infer<typeof configSchema>;

function deepMerge(target: any, source: any): any { const result = { ...target };

for (const key of Object.keys(source)) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = deepMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } }

return result; }

export function loadConfig(): Config { const env = process.env.NODE_ENV || 'development'; const configDir = join(process.cwd(), 'config');

// Load default config const defaultPath = join(configDir, 'default.json'); let config = JSON.parse(readFileSync(defaultPath, 'utf-8'));

// Merge environment-specific config const envPath = join(configDir, ${env}.json); if (existsSync(envPath)) { const envConfig = JSON.parse(readFileSync(envPath, 'utf-8')); config = deepMerge(config, envConfig); }

// Merge local overrides (not committed) const localPath = join(configDir, 'local.json'); if (existsSync(localPath)) { const localConfig = JSON.parse(readFileSync(localPath, 'utf-8')); config = deepMerge(config, localConfig); }

// Validate return configSchema.parse(config); }

export const config = loadConfig();

Secrets Management

Local Development Secrets

Use a secrets manager locally too

Option 1: 1Password CLI

op read "op://Development/MyApp/API_KEY"

Option 2: Doppler

doppler secrets download --no-file --format env > .env

Option 3: AWS SSM (for local AWS dev)

aws ssm get-parameter --name "/myapp/dev/api-key" --with-decryption --query "Parameter.Value"

Production Secrets with Vercel

Add secrets via CLI

vercel env add STRIPE_SECRET_KEY production vercel env add DATABASE_URL production

Pull secrets to local .env

vercel env pull .env.local

Secrets in Docker

docker-compose.yml

version: '3.8' services: app: build: . environment: - NODE_ENV=production env_file: - .env secrets: - db_password - api_key

secrets: db_password: file: ./secrets/db_password.txt api_key: external: true # From Docker Swarm/secrets manager

AWS Secrets Manager Integration

// src/config/secrets.ts import { SecretsManagerClient, GetSecretValueCommand, } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({ region: 'us-east-1' });

interface AppSecrets { database_url: string; stripe_secret_key: string; jwt_secret: string; }

let cachedSecrets: AppSecrets | null = null;

export async function getSecrets(): Promise<AppSecrets> { if (cachedSecrets) { return cachedSecrets; }

const secretId = process.env.AWS_SECRET_ID || 'myapp/production/secrets';

const command = new GetSecretValueCommand({ SecretId: secretId }); const response = await client.send(command);

if (!response.SecretString) { throw new Error('Secret not found'); }

cachedSecrets = JSON.parse(response.SecretString); return cachedSecrets!; }

// Usage async function connectDatabase() { const secrets = await getSecrets(); return createConnection(secrets.database_url); }

Feature Flags

Simple Feature Flag System

// src/config/features.ts import { env } from './env';

export const features = { analytics: env.ENABLE_ANALYTICS, betaFeatures: env.ENABLE_BETA_FEATURES, darkMode: true, // Always enabled

// Computed flags get isProduction() { return env.NODE_ENV === 'production'; },

get enableDebugLogs() { return env.NODE_ENV === 'development' || env.DEBUG === 'true'; }, } as const;

// Type-safe feature checking export function isEnabled(feature: keyof typeof features): boolean { return Boolean(features[feature]); }

// Usage if (isEnabled('analytics')) { initAnalytics(); }

Environment-Aware Feature Flags

// src/config/feature-flags.ts type Environment = 'development' | 'staging' | 'production';

interface FeatureConfig { enabled: boolean | Environment[]; description: string; }

const featureFlags: Record<string, FeatureConfig> = { newCheckout: { enabled: ['development', 'staging'], description: 'New checkout flow', }, aiAssistant: { enabled: true, description: 'AI assistant feature', }, experimentalApi: { enabled: ['development'], description: 'Experimental API endpoints', }, };

export function isFeatureEnabled( feature: keyof typeof featureFlags ): boolean { const config = featureFlags[feature]; const currentEnv = process.env.NODE_ENV as Environment;

if (typeof config.enabled === 'boolean') { return config.enabled; }

return config.enabled.includes(currentEnv); }

Configuration Patterns

Singleton Configuration

// src/config/index.ts import { env } from './env'; import { loadConfig } from './loader'; import { features } from './features';

class AppConfig { private static instance: AppConfig;

public readonly env = env; public readonly features = features; public readonly settings = loadConfig();

private constructor() { // Freeze to prevent modifications Object.freeze(this); }

static getInstance(): AppConfig { if (!AppConfig.instance) { AppConfig.instance = new AppConfig(); } return AppConfig.instance; }

// Helper methods get isDevelopment(): boolean { return this.env.NODE_ENV === 'development'; }

get isProduction(): boolean { return this.env.NODE_ENV === 'production'; }

get databaseUrl(): string { return this.env.DATABASE_URL; } }

export const config = AppConfig.getInstance();

Runtime Configuration Updates

// src/config/runtime.ts import { EventEmitter } from 'events';

class RuntimeConfig extends EventEmitter { private values: Map<string, any> = new Map();

get<T>(key: string, defaultValue?: T): T | undefined { return this.values.get(key) ?? defaultValue; }

set<T>(key: string, value: T): void { const oldValue = this.values.get(key); this.values.set(key, value); this.emit('change', { key, oldValue, newValue: value }); }

// Subscribe to changes onChange(callback: (change: { key: string; oldValue: any; newValue: any }) => void) { this.on('change', callback); return () => this.off('change', callback); } }

export const runtimeConfig = new RuntimeConfig();

// Usage: Update config at runtime runtimeConfig.set('maintenanceMode', true);

// Usage: React to changes runtimeConfig.onChange(({ key, newValue }) => { if (key === 'maintenanceMode' && newValue) { showMaintenanceBanner(); } });

Validation Checklist

Environment Setup

  • .env.example with all variables documented

  • .gitignore excludes all .env files except example

  • Validation runs at application startup

  • Helpful error messages for missing/invalid config

  • Type definitions for all config values

Security

  • No secrets in version control

  • Secrets use appropriate secrets manager

  • Production secrets rotated regularly

  • Minimal exposure of sensitive values in logs

  • NEXT_PUBLIC_ prefix only for truly public values

Developer Experience

  • Easy local setup (copy .env.example )

  • Clear documentation of each variable

  • Defaults for development environment

  • Config validation gives actionable errors

  • IDE autocomplete for config values

Multi-Environment

  • Separate configs for dev/staging/production

  • Environment-specific overrides work correctly

  • Feature flags for gradual rollouts

  • Easy switching between environments

When to Use This Skill

Invoke this skill when:

  • Setting up configuration for a new project

  • Adding new environment variables

  • Implementing secrets management

  • Creating feature flag systems

  • Debugging configuration issues

  • Setting up multi-environment deployments

  • Migrating configuration between providers

  • Implementing runtime configuration changes

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

supabase-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

landing-page-designer

No summary provided by upstream source.

Repository SourceNeeds Review
General

appstore-readiness

No summary provided by upstream source.

Repository SourceNeeds Review