Agent skill
env-config
Use when setting up environment configuration, loading .env files, or managing application settings across environments. Triggers for: .env setup, loading environment variables, Pydantic BaseSettings, configuration validation, or secret management. NOT for: code-specific logic, feature flags, or runtime feature toggles.
Install this agent skill to your Project
npx add-skill https://github.com/aiskillstore/marketplace/tree/main/skills/awais68/env-config
SKILL.md
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 | load_dotenv() at application start |
| Access var | settings.DB_URL, settings.JWT_SECRET |
| Required var | Field(..., description="Database URL") |
| Optional var | DB_HOST: str = "localhost" |
| Secret type | SecretStr 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
Field(..., ...)validation - No leaks in logs: Use
SecretStrfor sensitive values, never print settings - .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-auth |
JWT_SECRET_KEY from settings |
@sqlmodel-crud |
DB_URL from settings |
@fastapi-app |
All app settings from settings |
@db-migration |
Database URL for migrations |
@api-route-design |
API prefix, CORS origins |
Security Best Practices
DO
- Use
SecretStrfor passwords, API keys, tokens - Rotate secrets regularly
- Use different secrets per environment
- Store production secrets in secrets manager
- Validate all required settings at startup
DON'T
- Never commit
.envfiles to version control - 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)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
perigon-backend
Perigon ASP.NET Core + EF Core + Aspire conventions
perigon-agent
Pointers for Copilot/agents to apply Perigon conventions
perigon-angular
Angular 21+ standalone/Material/signal conventions for Perigon WebApp
fastapi-mastery
Comprehensive FastAPI development skill covering REST API creation, routing, request/response handling, validation, authentication, database integration, middleware, and deployment. Use when working with FastAPI projects, building APIs, implementing CRUD operations, setting up authentication/authorization, integrating databases (SQL/NoSQL), adding middleware, handling WebSockets, or deploying FastAPI applications. Triggered by requests involving .py files with FastAPI code, API endpoint creation, Pydantic models, or FastAPI-specific features.
context7-efficient
Token-efficient library documentation fetcher using Context7 MCP with 86.8% token savings through intelligent shell pipeline filtering. Fetches code examples, API references, and best practices for JavaScript, Python, Go, Rust, and other libraries. Use when users ask about library documentation, need code examples, want API usage patterns, are learning a new framework, need syntax reference, or troubleshooting with library-specific information. Triggers include questions like "Show me React hooks", "How do I use Prisma", "What's the Next.js routing syntax", or any request for library/framework documentation.
browser-use
Browser automation using Playwright MCP. Navigate websites, fill forms, click elements, take screenshots, and extract data. Use when tasks require web browsing, form submission, web scraping, UI testing, or any browser interaction.
Didn't find tool you were looking for?