Marketplace env-config
install
source · Clone the upstream repo
git clone https://github.com/aiskillstore/marketplace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/awais68/env-config" ~/.claude/skills/aiskillstore-marketplace-env-config-864180 && rm -rf "$T"
manifest:
skills/awais68/env-config/SKILL.mdsource content
Environment Configuration Skill
Expert environment configuration management for Python/FastAPI projects with secure secrets handling and multi-environment support.
Quick Reference
| Pattern | Usage |
|---|---|
| Load .env | at application start |
| Access var | , |
| Required var | |
| Optional var | |
| Secret type | for sensitive values |
Project Structure
project/ ├── .env # Local development (NOT committed) ├── .env.example # Template with all required vars (committed) ├── .env.staging # Staging environment ├── .env.production # Production environment (managed by infra) └── config/ ├── __init__.py └── settings.py # Pydantic BaseSettings
settings.py - Base Configuration
# config/settings.py from functools import lru_cache from pydantic import Field, SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", extra="ignore", ) # Application APP_NAME: str = "ERP System" DEBUG: bool = False API_V1_PREFIX: str = "/v1" # Database DB_URL: str = Field( ..., description="PostgreSQL connection URL", examples=["postgresql://user:pass@localhost:5432/dbname"], ) DB_POOL_SIZE: int = Field(default=5, ge=1, le=100) # JWT Authentication JWT_SECRET_KEY: SecretStr = Field( ..., description="Secret key for JWT signing", ) JWT_ALGORITHM: str = "HS256" JWT_EXPIRATION_MINUTES: int = Field(default=15, ge=1) # Redis (Optional) REDIS_URL: str | None = None # Logging LOG_LEVEL: str = Field(default="INFO", pattern="^(DEBUG|INFO|WARNING|ERROR)$") # CORS CORS_ORIGINS: list[str] = ["http://localhost:3000"] @property def is_production(self) -> bool: return not self.DEBUG @lru_cache def get_settings() -> Settings: """Cached settings instance for application lifecycle.""" return Settings()
.env.example - Template File
# .env.example - Copy to .env and fill in values # DO NOT commit actual secrets! # Application APP_NAME="ERP System" DEBUG=false API_V1_PREFIX="/v1" # Database (required) DB_URL="postgresql://user:password@localhost:5432/erp_db" # JWT Authentication (required - generate with: openssl rand -hex 32) JWT_SECRET_KEY="your-secret-key-here-generate-with-openssl-rand-hex-32" JWT_ALGORITHM="HS256" JWT_EXPIRATION_MINUTES=15 # Redis (optional) # REDIS_URL="redis://localhost:6379/0" # Logging LOG_LEVEL="INFO" # CORS CORS_ORIGINS="http://localhost:3000"
.env - Local Development
# .env - Local development only # NEVER commit this file to version control APP_NAME="ERP System" DEBUG=true API_V1_PREFIX="/v1" # Local PostgreSQL DB_URL="postgresql://postgres:postgres@localhost:5432/erp_dev" # Generate with: openssl rand -hex 32 JWT_SECRET_KEY="local-dev-secret-key-change-in-production" JWT_ALGORITHM="HS256" JWT_EXPIRATION_MINUTES=15 # Local Redis (if using) REDIS_URL="redis://localhost:6379/0" LOG_LEVEL="DEBUG" CORS_ORIGINS="http://localhost:3000,http://localhost:5173"
Usage in Application
FastAPI Application
# main.py from contextlib import asynccontextmanager from dotenv import load_dotenv load_dotenv() # Load .env file from config.settings import get_settings @asynccontextmanager async def lifespan(app): settings = get_settings() print(f"Starting {settings.APP_NAME} in {'DEBUG' if settings.DEBUG else 'PROD'} mode") yield print("Shutting down...") app = FastAPI( title=get_settings().APP_NAME, lifespan=lifespan, ) # Include routers from app.routers import fees, students app.include_router(fees.router, prefix=get_settings().API_V1_PREFIX) app.include_router(students.router, prefix=get_settings().API_V1_PREFIX)
Database Connection
# database.py from sqlmodel import create_engine, Session from config.settings import get_settings settings = get_settings() engine = create_engine( settings.DB_URL.get_secret_value() if hasattr(settings.DB_URL, 'get_secret_value') else settings.DB_URL, pool_size=settings.DB_POOL_SIZE, max_overflow=10, ) def get_session(): with Session(engine) as session: yield session
JWT Configuration
# auth/jwt.py from datetime import timedelta from config.settings import get_settings settings = get_settings() JWT_SECRET = settings.JWT_SECRET_KEY.get_secret_value() JWT_ALGORITHM = settings.JWT_ALGORITHM ACCESS_TOKEN_EXPIRE_MINUTES = settings.JWT_EXPIRATION_MINUTES def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str: # ... token creation logic pass
Multi-Environment Support
Environment-Specific Configs
# config/settings.py class Settings(BaseSettings): # ... shared settings @classmethod def from_env(cls, env: str = "development") -> "Settings": """Load settings for specific environment.""" env_file = { "development": ".env", "staging": ".env.staging", "production": ".env.production", }.get(env, ".env") return cls(_env_file=env_file)
Production Override
# Production should use environment variables, not .env files # Set these in your deployment platform (Docker, K8s, Cloud Run, etc.) export DB_URL="postgresql://prod_user:prod_pass@prod-db.example.com:5432/erp_prod" export JWT_SECRET_KEY="production-secret-key-from-secrets-manager" export DEBUG=false export LOG_LEVEL="WARNING"
Secret Management
Generate Secrets
# Generate secure random secret openssl rand -hex 32 # For JWT_SECRET_KEY # Generate database password openssl rand -base64 32
Secret Rotation Script
# scripts/rotate_secret.py """Rotate a secret in all environments.""" import os import re def rotate_secret(env_file: str, key: str, new_value: str): """Replace secret value in .env file.""" with open(env_file, "r") as f: content = f.read() # Pattern to match KEY=value pattern = f"^{key}=.*$" replacement = f"{key}={new_value}" new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE) with open(env_file, "w") as f: f.write(new_content) print(f"Rotated {key} in {env_file}") if __name__ == "__main__": import sys if len(sys.argv) != 4: print("Usage: rotate_secret.py <env_file> <key> <new_value>") sys.exit(1) rotate_secret(sys.argv[1], sys.argv[2], sys.argv[3])
Quality Checklist
- Validation on load: All required fields have
validationField(..., ...) - No leaks in logs: Use
for sensitive values, never print settingsSecretStr - .env.example committed: Template shows all required variables
- .env.gitignored: Local development file excluded from version control
- Secrets generated: JWT secrets use
openssl rand -hex 32 - Environment isolation: Different configs for dev/staging/production
- Type safety: All settings have proper type annotations
Integration with Other Skills
| Skill | Integration Point |
|---|---|
| JWT_SECRET_KEY from settings |
| DB_URL from settings |
| All app settings from settings |
| Database URL for migrations |
| API prefix, CORS origins |
Security Best Practices
DO
- Use
for passwords, API keys, tokensSecretStr - Rotate secrets regularly
- Use different secrets per environment
- Store production secrets in secrets manager
- Validate all required settings at startup
DON'T
- Never commit
files to version control.env - Never hardcode secrets in source code
- Never log settings or environment variables
- Never use default/placeholder secrets in production
- Never expose configuration details in error messages
Startup Validation
# config/validate.py """Validate required configuration at startup.""" from pydantic import ValidationError from config.settings import Settings def validate_settings() -> bool: """Ensure all required settings are configured.""" try: settings = Settings() return True except ValidationError as e: print("Configuration validation failed:") for error in e.errors(): print(f" - {error['loc'][0]}: {error['msg']}") return False if __name__ == "__main__": if not validate_settings(): exit(1)