Environment Configuration Skill Overview This skill provides comprehensive guidance for managing environment configurations, secrets, and environment variables in Python projects using UV (the modern Python package and project manager). It covers secure configuration patterns, multi-environment setups, .env file management, and secrets handling with encryption support. Environment configuration is critical for separating configuration from code (12-factor app principles), managing secrets securely across environments, preventing credential leaks, and supporting team collaboration. When to Use…

\nextend-exclude = '''\n/(\n \\.git\n | \\.venv\n | build\n | dist\n)/\n'''\n\n# Ruff linter\n[tool.ruff]\nline-length = 100\ntarget-version = \"py310\"\n\nselect = [\n \"E\", # pycodestyle errors\n \"W\", # pycodestyle warnings\n \"F\", # pyflakes\n \"I\", # isort\n \"C\", # flake8-comprehensions\n \"B\", # flake8-bugbear\n \"UP\", # pyupgrade\n]\n\nignore = [\n \"E501\", # line too long (handled by black)\n \"B008\", # do not perform function calls in argument defaults\n \"C901\", # too complex\n]\n\n# Exclude directories\nexclude = [\n \".git\",\n \".venv\",\n \"__pycache__\",\n \"build\",\n \"dist\",\n]\n\n# Ruff fixes\n[tool.ruff.per-file-ignores]\n\"__init__.py\" = [\"F401\"] # Allow unused imports in __init__.py\n\"tests/*\" = [\"F401\", \"F811\"] # Allow unused imports and redefinitions in tests\n\n# MyPy type checker\n[tool.mypy]\npython_version = \"3.10\"\nwarn_return_any = true\nwarn_unused_configs = true\ndisallow_untyped_defs = false\ndisallow_incomplete_defs = false\ncheck_untyped_defs = true\nno_implicit_optional = true\nwarn_redundant_casts = true\nwarn_unused_ignores = true\nwarn_no_return = true\nstrict_optional = true\n\n# Coverage configuration\n[tool.coverage.run]\nsource = [\"src\"]\nomit = [\n \"*/tests/*\",\n \"*/__pycache__/*\",\n \"*/.venv/*\",\n]\n\n[tool.coverage.report]\nexclude_lines = [\n \"pragma: no cover\",\n \"def __repr__\",\n \"raise AssertionError\",\n \"raise NotImplementedError\",\n \"if __name__ == .__main__.:\",\n \"if TYPE_CHECKING:\",\n]\n\n# Example UV lock file configuration\n# UV automatically handles dependency resolution and locking\n# Run 'uv lock' to generate uv.lock file\n# Run 'uv sync' to install from lock file\n","content_type":"text/plain; charset=utf-8","language":"toml","size":4158,"content_sha256":"472e2f711d7176ab59883c13338596a1d940af78968aac385fb9e8ac7f363f24"},{"filename":"examples/secrets_template.json","content":"{\n \"_comment\": \"Secrets Configuration Template\",\n \"_instructions\": \"Copy this file to secrets.json and fill in your actual values. Add secrets.json to .gitignore!\",\n \"_format\": \"All values should be strings. Use environment variables as fallback.\",\n\n \"api_keys\": {\n \"anthropic_api_key\": \"sk-ant-api03-your-key-here\",\n \"openai_api_key\": \"sk-your-key-here\",\n \"openrouter_api_key\": \"sk-or-v1-your-key-here\",\n \"huggingface_token\": \"\",\n \"cohere_api_key\": \"\"\n },\n\n \"database\": {\n \"url\": \"postgresql://user:password@localhost:5432/myapp\",\n \"username\": \"dbuser\",\n \"password\": \"secure-password-here\",\n \"host\": \"localhost\",\n \"port\": \"5432\",\n \"database\": \"myapp_production\"\n },\n\n \"redis\": {\n \"url\": \"redis://localhost:6379/0\",\n \"password\": \"\"\n },\n\n \"email\": {\n \"smtp_host\": \"smtp.gmail.com\",\n \"smtp_port\": \"587\",\n \"smtp_user\": \"[email protected]\",\n \"smtp_password\": \"your-app-specific-password\",\n \"from_email\": \"[email protected]\"\n },\n\n \"security\": {\n \"secret_key\": \"generate-a-random-secret-key-here\",\n \"jwt_secret\": \"another-random-secret-for-jwt\",\n \"encryption_key\": \"base64-encoded-32-byte-key-here\"\n },\n\n \"cloud_services\": {\n \"aws\": {\n \"access_key_id\": \"AKIA...\",\n \"secret_access_key\": \"your-secret-key\",\n \"region\": \"us-east-1\",\n \"s3_bucket\": \"your-bucket-name\"\n },\n \"gcp\": {\n \"project_id\": \"your-project-id\",\n \"credentials_path\": \"path/to/service-account.json\"\n },\n \"azure\": {\n \"connection_string\": \"DefaultEndpointsProtocol=https;...\",\n \"container_name\": \"your-container\"\n }\n },\n\n \"monitoring\": {\n \"sentry_dsn\": \"https://[email protected]/xxx\",\n \"newrelic_license_key\": \"\",\n \"datadog_api_key\": \"\"\n },\n\n \"payment\": {\n \"stripe\": {\n \"public_key\": \"pk_live_...\",\n \"secret_key\": \"sk_live_...\",\n \"webhook_secret\": \"whsec_...\"\n },\n \"paypal\": {\n \"client_id\": \"\",\n \"client_secret\": \"\",\n \"mode\": \"live\"\n }\n },\n\n \"oauth\": {\n \"google\": {\n \"client_id\": \"xxx.apps.googleusercontent.com\",\n \"client_secret\": \"your-client-secret\"\n },\n \"github\": {\n \"client_id\": \"Iv1.xxx\",\n \"client_secret\": \"your-client-secret\"\n },\n \"facebook\": {\n \"app_id\": \"\",\n \"app_secret\": \"\"\n }\n },\n\n \"custom\": {\n \"api_endpoint\": \"https://api.yourservice.com\",\n \"webhook_url\": \"https://yourapp.com/webhook\",\n \"shared_secret\": \"shared-secret-between-services\"\n },\n\n \"_generation_info\": {\n \"created\": \"2024-01-01\",\n \"version\": \"1.0.0\",\n \"environment\": \"template\"\n }\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":2591,"content_sha256":"5b0216323e4aa8b8797769b05d888f433ca37fcbd9c5eac51b8ca252f6c12129"},{"filename":"README.md","content":"# Environment Configuration Skill\n\nComprehensive environment configuration and secrets management for Python projects using UV.\n\n## Overview\n\nThis skill provides:\n- **UV Integration** - Modern Python package management\n- **.env File Management** - Parse, validate, and secure .env files\n- **Secrets Encryption** - Encrypt sensitive configuration files\n- **Multi-Environment Support** - Separate dev/staging/production configs\n- **Security Best Practices** - Prevent credential leaks and ensure secure defaults\n\n## Quick Start\n\n### 1. Install UV\n\n```bash\n# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Or with pip\npip install uv\n```\n\n### 2. Set Up Your Project\n\n```bash\n# Create new project\nuv init my-project\ncd my-project\n\n# Create virtual environment\nuv venv\nsource .venv/bin/activate # macOS/Linux\n\n# Install dependencies\nuv add python-dotenv pydantic cryptography\n```\n\n### 3. Configure Environment\n\n```bash\n# Copy the example template\ncp examples/.env.example .env\n\n# Edit .env with your actual values\n# nano .env\n```\n\n### 4. Add to .gitignore\n\n```bash\necho \".env\" >> .gitignore\necho \"secrets.json\" >> .gitignore\n```\n\n## File Structure\n\n```\nenv-config/\n├── SKILL.md # Main skill documentation\n├── README.md # This file\n├── scripts/\n│ └── env_helper.py # Environment management utilities\n└── examples/\n ├── .env.example # Comprehensive .env template\n ├── pyproject.toml # UV project configuration\n └── secrets_template.json # JSON secrets template\n```\n\n## Usage\n\n### Using env_helper.py\n\nThe helper script provides utilities for managing environment files:\n\n```bash\n# Validate .env file\npython scripts/env_helper.py validate .env --required APP_NAME DATABASE_URL\n\n# Check for security issues\npython scripts/env_helper.py check .env\n\n# Compare two environments\npython scripts/env_helper.py compare .env.development .env.production\n\n# Generate template from existing .env\npython scripts/env_helper.py template .env .env.template\n\n# Encrypt secrets file\npython scripts/env_helper.py encrypt secrets.json secrets.encrypted\n\n# Decrypt secrets\npython scripts/env_helper.py decrypt secrets.encrypted --output secrets.json\n\n# Merge env files\npython scripts/env_helper.py merge .env.base .env.local .env\n```\n\n### In Your Application\n\n```python\n# config.py\nfrom pathlib import Path\nfrom dotenv import load_dotenv\nimport os\n\n# Load environment variables\nload_dotenv()\n\nclass Config:\n \"\"\"Application configuration.\"\"\"\n\n APP_NAME = os.getenv('APP_NAME', 'MyApp')\n DEBUG = os.getenv('DEBUG', 'false').lower() == 'true'\n DATABASE_URL = os.getenv('DATABASE_URL')\n ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY')\n\nconfig = Config()\n```\n\n```python\n# main.py\nfrom config import config\n\ndef main():\n print(f\"Starting {config.APP_NAME}\")\n print(f\"Debug mode: {config.DEBUG}\")\n\n if not config.ANTHROPIC_API_KEY:\n print(\"Warning: ANTHROPIC_API_KEY not set\")\n\nif __name__ == \"__main__\":\n main()\n```\n\n## Key Features\n\n### Multi-Environment Support\n\nCreate environment-specific files:\n- `.env.development` - Local development\n- `.env.staging` - Staging environment\n- `.env.production` - Production (never commit!)\n\n### Secrets Encryption\n\nEncrypt sensitive files:\n```python\nfrom scripts.env_helper import encrypt_secrets, decrypt_secrets\n\n# Encrypt\nencrypt_secrets('secrets.json', 'secrets.encrypted', 'password')\n\n# Decrypt\nsecrets = decrypt_secrets('secrets.encrypted', 'password')\n```\n\n### Configuration Validation\n\n```python\nfrom config import Config, ConfigError\n\ntry:\n config = Config(env='production')\nexcept ConfigError as e:\n print(f\"Configuration error: {e}\")\n sys.exit(1)\n```\n\n## UV Commands\n\n```bash\n# Install dependencies\nuv sync\n\n# Add new package\nuv add package-name\n\n# Add dev dependency\nuv add --dev pytest\n\n# Update dependencies\nuv lock --upgrade\n\n# Run application\nuv run python main.py\n\n# Run tests\nuv run pytest\n```\n\n## Security Best Practices\n\n1. **Never commit secrets** - Add `.env` and `secrets.json` to `.gitignore`\n2. **Use strong secrets** - Generate random keys for production\n3. **Separate environments** - Different configs for dev/staging/prod\n4. **Validate on startup** - Check required variables exist\n5. **Rotate regularly** - Change API keys and secrets periodically\n6. **Encrypt at rest** - Use encryption for sensitive files\n7. **Audit regularly** - Run security checks on configuration\n\n## Examples\n\nSee the `examples/` directory for:\n- Comprehensive .env template with all common variables\n- UV pyproject.toml configuration\n- JSON secrets template structure\n\n## Documentation\n\nRead `SKILL.md` for:\n- Complete workflow guide\n- Advanced configuration patterns\n- Testing strategies\n- Troubleshooting tips\n- Best practices\n\n## Requirements\n\n- Python 3.10+\n- UV package manager\n- python-dotenv\n- cryptography (for encryption features)\n- pydantic (for type-safe config)\n\n## License\n\nMIT License - See individual project files for details.\n\n## Contributing\n\nThis skill is part of the Claude Skills library. For improvements or issues, please refer to the main repository guidelines.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5196,"content_sha256":"93106ee4006b23e67f8dfdb380cdf6ed20d34f276d7ee67a28b4c91cbd7caea7"},{"filename":"references/advanced-topics.md","content":"# Advanced Configuration Topics\n\n## Secrets Encryption\n\n### Encryption Implementation\n\nFor highly sensitive environments, encrypt secrets at rest:\n\n```python\n# In your app\nfrom scripts.env_helper import encrypt_secrets, decrypt_secrets\n\n# Encrypt secrets file\nencrypt_secrets('secrets.json', 'secrets.encrypted', 'your-encryption-password')\n\n# Decrypt at runtime\nsecrets = decrypt_secrets('secrets.encrypted', 'your-encryption-password')\n```\n\nSee `scripts/env_helper.py` for the complete encryption utilities implementation.\n\n### Encryption Best Practices\n\n1. **Key Management**\n - Store encryption passwords in secure key management systems\n - Rotate encryption keys regularly\n - Use different keys for different environments\n\n2. **Storage**\n - Keep encrypted files in version control\n - Store decryption keys outside the codebase\n - Use environment variables for decryption keys\n\n3. **Access Control**\n - Limit who can decrypt production secrets\n - Log all decryption attempts\n - Use time-limited access tokens\n\n## Secret Rotation\n\n### API Key Rotation Process\n\n```python\ndef rotate_api_key(old_key: str, new_key: str):\n \"\"\"\n Rotate API key gracefully.\n\n 1. Add new key to environment\n 2. Update all services to use new key\n 3. Verify new key works\n 4. Remove old key\n \"\"\"\n # Load current config\n env_file = Path('.env')\n content = env_file.read_text()\n\n # Replace old key with new\n updated = content.replace(old_key, new_key)\n\n # Backup old config\n backup = Path('.env.backup')\n backup.write_text(content)\n\n # Write new config\n env_file.write_text(updated)\n\n print(\"✓ API key rotated. Backup saved to .env.backup\")\n```\n\n### Rotation Checklist\n\n- [ ] Generate new API key from provider\n- [ ] Update .env file with new key\n- [ ] Restart application with new key\n- [ ] Verify new key works correctly\n- [ ] Revoke old key at provider\n- [ ] Remove backup file after verification\n- [ ] Update team documentation\n\n## Environment Variable Auditing\n\n### Security Audit Function\n\n```python\ndef audit_environment():\n \"\"\"Audit environment variables for security issues.\"\"\"\n issues = []\n\n # Check for default/example values\n dangerous_patterns = [\n 'xxx',\n 'example',\n 'test123',\n 'password',\n 'changeme',\n ]\n\n for key, value in os.environ.items():\n if any(pattern in value.lower() for pattern in dangerous_patterns):\n issues.append(f\"⚠ {key} appears to have a default/test value\")\n\n # Check for required keys in production\n if os.getenv('APP_ENV') == 'production':\n required = ['SECRET_KEY', 'DATABASE_URL']\n for key in required:\n if not os.getenv(key):\n issues.append(f\"❌ Required key {key} not set in production\")\n\n if issues:\n print(\"Security Issues Found:\")\n for issue in issues:\n print(f\" {issue}\")\n else:\n print(\"✓ No security issues detected\")\n```\n\n### Regular Audit Schedule\n\nRun security audits:\n- Before every production deployment\n- Weekly in staging environments\n- After any configuration changes\n- During security reviews\n\n### Audit Checklist\n\n- [ ] No default/example values in use\n- [ ] All required variables set for environment\n- [ ] No secrets in environment variable names\n- [ ] Proper access controls on .env files\n- [ ] No secrets logged or printed\n- [ ] Backup configurations secured\n- [ ] Team members have proper access levels\n\n## Multi-Tenant Configuration\n\n### Per-Tenant Settings\n\n```python\nclass TenantConfig(Config):\n \"\"\"Multi-tenant configuration support.\"\"\"\n\n def __init__(self, tenant_id: str, env: str = None):\n self.tenant_id = tenant_id\n super().__init__(env)\n\n def _load_env_file(self):\n \"\"\"Load tenant-specific configuration.\"\"\"\n # Load base environment config\n super()._load_env_file()\n\n # Load tenant-specific overrides\n tenant_file = Path(f'.env.{self.env}.{self.tenant_id}')\n if tenant_file.exists():\n load_dotenv(tenant_file, override=True)\n print(f\"✓ Loaded tenant config from {tenant_file}\")\n\n @property\n def database_url(self) -> str:\n \"\"\"Tenant-specific database URL.\"\"\"\n return os.getenv(\n f'{self.tenant_id.upper()}_DATABASE_URL',\n os.getenv('DATABASE_URL')\n )\n\n\n# Usage\nconfig = TenantConfig(tenant_id='acme-corp', env='production')\n```\n\n### Tenant Isolation Patterns\n\n1. **Database per Tenant**\n ```bash\n # .env.production.acme-corp\n ACME_CORP_DATABASE_URL=postgresql://host/acme_corp_db\n ```\n\n2. **API Keys per Tenant**\n ```bash\n # .env.production.widget-inc\n WIDGET_INC_API_KEY=sk-xxx\n ```\n\n3. **Feature Flags per Tenant**\n ```bash\n # .env.production.startup-xyz\n STARTUP_XYZ_ENABLE_BETA_FEATURES=true\n ```\n\n## Configuration Versioning\n\n### Versioned Config Pattern\n\n```python\nclass VersionedConfig(Config):\n \"\"\"Configuration with version tracking.\"\"\"\n\n CONFIG_VERSION = \"2.0.0\"\n\n def __init__(self, env: str = None):\n super().__init__(env)\n self._check_version_compatibility()\n\n def _check_version_compatibility(self):\n \"\"\"Verify config version matches application version.\"\"\"\n env_version = os.getenv('CONFIG_VERSION', '1.0.0')\n\n if env_version != self.CONFIG_VERSION:\n print(f\"⚠ Config version mismatch: expected {self.CONFIG_VERSION}, got {env_version}\")\n print(\" Consider updating .env file to latest version\")\n\n @classmethod\n def migrate_from_v1(cls, old_config_path: Path):\n \"\"\"Migrate configuration from version 1.x to 2.x.\"\"\"\n old_content = old_config_path.read_text()\n new_content = old_content.replace(\n 'OLD_VAR_NAME',\n 'NEW_VAR_NAME'\n )\n # Add new required variables\n new_content += \"\\nCONFIG_VERSION=2.0.0\\n\"\n new_config_path = old_config_path.with_suffix('.env.v2')\n new_config_path.write_text(new_content)\n print(f\"✓ Migrated config to {new_config_path}\")\n```\n\n## Dynamic Configuration Reloading\n\n### Hot Reload Support\n\n```python\nimport signal\nimport threading\nimport time\n\nclass ReloadableConfig(Config):\n \"\"\"Configuration that can be reloaded without restart.\"\"\"\n\n def __init__(self, env: str = None, watch: bool = False):\n super().__init__(env)\n if watch:\n self._start_watcher()\n\n def _start_watcher(self):\n \"\"\"Watch .env file for changes and reload.\"\"\"\n def watch_loop():\n last_mtime = Path('.env').stat().st_mtime\n while True:\n time.sleep(5) # Check every 5 seconds\n current_mtime = Path('.env').stat().st_mtime\n if current_mtime != last_mtime:\n print(\"ℹ .env file changed, reloading...\")\n self._load_env_file()\n last_mtime = current_mtime\n\n watcher_thread = threading.Thread(target=watch_loop, daemon=True)\n watcher_thread.start()\n\n def reload(self):\n \"\"\"Manually reload configuration.\"\"\"\n self._load_env_file()\n self._validate_required()\n print(\"✓ Configuration reloaded\")\n\n\n# Usage\nconfig = ReloadableConfig(watch=True)\n\n# Or manual reload\n# config.reload()\n```\n\n## Configuration Templates\n\n### Template Generation\n\n```python\ndef generate_env_template(config_class: type, output_file: str = '.env.template'):\n \"\"\"Generate .env.template from Config class properties.\"\"\"\n template_lines = [\n \"# Environment Configuration Template\",\n f\"# Generated from {config_class.__name__}\",\n \"\",\n ]\n\n # Extract properties from config class\n for name, prop in vars(config_class).items():\n if isinstance(prop, property):\n env_var = name.upper()\n template_lines.append(f\"{env_var}=\")\n\n template_content = \"\\n\".join(template_lines)\n Path(output_file).write_text(template_content)\n print(f\"✓ Template written to {output_file}\")\n```\n\n## Docker Integration\n\n### Docker Compose with Env Files\n\n```yaml\n# docker-compose.yml\nversion: '3.8'\nservices:\n app:\n build: .\n env_file:\n - .env\n - .env.${APP_ENV:-development}\n environment:\n - APP_ENV=${APP_ENV:-development}\n```\n\n### Multi-Stage Docker Builds\n\n```dockerfile\n# Dockerfile\nFROM python:3.11-slim as base\n\n# Install UV\nRUN pip install uv\n\n# Development stage\nFROM base as development\nENV APP_ENV=development\nCOPY .env.development /app/.env\nWORKDIR /app\nRUN uv sync\n\n# Production stage\nFROM base as production\nENV APP_ENV=production\n# Don't copy .env files - use runtime env vars\nWORKDIR /app\nRUN uv sync --no-dev\n```\n\n### Runtime Environment Injection\n\n```bash\n# Run with environment variables\ndocker run -e APP_ENV=production \\\n -e DATABASE_URL=$PROD_DB_URL \\\n -e ANTHROPIC_API_KEY=$PROD_API_KEY \\\n myapp:latest\n\n# Or use env file\ndocker run --env-file .env.production myapp:latest\n```\n\n## Cloud Platform Integration\n\n### AWS Secrets Manager\n\n```python\nimport boto3\nfrom botocore.exceptions import ClientError\n\ndef load_from_aws_secrets(secret_name: str, region: str = 'us-east-1') -> dict:\n \"\"\"Load secrets from AWS Secrets Manager.\"\"\"\n client = boto3.client('secretsmanager', region_name=region)\n\n try:\n response = client.get_secret_value(SecretId=secret_name)\n return json.loads(response['SecretString'])\n except ClientError as e:\n print(f\"Error loading secret: {e}\")\n return {}\n```\n\n### Google Cloud Secret Manager\n\n```python\nfrom google.cloud import secretmanager\n\ndef load_from_gcp_secrets(project_id: str, secret_id: str) -> str:\n \"\"\"Load secret from Google Cloud Secret Manager.\"\"\"\n client = secretmanager.SecretManagerServiceClient()\n name = f\"projects/{project_id}/secrets/{secret_id}/versions/latest\"\n\n response = client.access_secret_version(request={\"name\": name})\n return response.payload.data.decode('UTF-8')\n```\n\n### Azure Key Vault\n\n```python\nfrom azure.identity import DefaultAzureCredential\nfrom azure.keyvault.secrets import SecretClient\n\ndef load_from_azure_keyvault(vault_url: str, secret_name: str) -> str:\n \"\"\"Load secret from Azure Key Vault.\"\"\"\n credential = DefaultAzureCredential()\n client = SecretClient(vault_url=vault_url, credential=credential)\n\n secret = client.get_secret(secret_name)\n return secret.value\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10428,"content_sha256":"0dbd4604ade0cfe4af4582397edd2aa9201c3701ce524e3abf8c69f7e8b4ced2"},{"filename":"references/api-reference.md","content":"# Configuration API Reference\n\n## Config Class Implementation\n\n### Basic Config Class\n\n```python\n\"\"\"\nEnvironment configuration management with UV.\nLoads and validates environment variables.\n\"\"\"\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Optional\nfrom dotenv import load_dotenv\n\n\nclass ConfigError(Exception):\n \"\"\"Raised when required configuration is missing.\"\"\"\n pass\n\n\nclass Config:\n \"\"\"Application configuration from environment variables.\"\"\"\n\n def __init__(self, env: str = None):\n \"\"\"\n Initialize configuration.\n\n Args:\n env: Environment name (development, staging, production)\n If None, uses APP_ENV environment variable\n \"\"\"\n # Determine environment\n self.env = env or os.getenv('APP_ENV', 'development')\n\n # Load environment-specific .env file\n self._load_env_file()\n\n # Validate required variables\n self._validate_required()\n\n def _load_env_file(self):\n \"\"\"Load appropriate .env file based on environment.\"\"\"\n # Try environment-specific file first\n env_file = Path(f'.env.{self.env}')\n if env_file.exists():\n load_dotenv(env_file, override=True)\n print(f\"✓ Loaded configuration from {env_file}\")\n\n # Then load .env (can override)\n if Path('.env').exists():\n load_dotenv('.env', override=False) # Don't override env-specific\n print(\"✓ Loaded configuration from .env\")\n\n def _validate_required(self):\n \"\"\"Validate that required environment variables are set.\"\"\"\n required = self.get_required_vars()\n missing = [var for var in required if not os.getenv(var)]\n\n if missing:\n raise ConfigError(\n f\"Missing required environment variables: {', '.join(missing)}\\n\"\n f\"Please check .env.template for required configuration.\"\n )\n\n @staticmethod\n def get_required_vars() -> list[str]:\n \"\"\"\n Define required environment variables.\n Override this in subclasses for custom requirements.\n \"\"\"\n return [\n 'APP_NAME',\n 'APP_ENV',\n ]\n\n # Application Settings\n @property\n def app_name(self) -> str:\n return os.getenv('APP_NAME', 'MyApp')\n\n @property\n def app_env(self) -> str:\n return self.env\n\n @property\n def debug(self) -> bool:\n return os.getenv('DEBUG', 'false').lower() in ('true', '1', 'yes')\n\n @property\n def log_level(self) -> str:\n return os.getenv('LOG_LEVEL', 'INFO')\n\n # Database\n @property\n def database_url(self) -> Optional[str]:\n return os.getenv('DATABASE_URL')\n\n # API Keys\n @property\n def anthropic_api_key(self) -> Optional[str]:\n return os.getenv('ANTHROPIC_API_KEY')\n\n @property\n def openai_api_key(self) -> Optional[str]:\n return os.getenv('OPENAI_API_KEY')\n\n # Security\n @property\n def secret_key(self) -> str:\n key = os.getenv('SECRET_KEY')\n if not key and not self.debug:\n raise ConfigError(\"SECRET_KEY must be set in production\")\n return key or 'dev-secret-key-change-in-production'\n\n\n# Global config instance\nconfig = Config()\n\n\n# Helper function for getting env vars with defaults\ndef get_env(key: str, default: str = None, required: bool = False) -> str:\n \"\"\"\n Get environment variable with optional default and validation.\n\n Args:\n key: Environment variable name\n default: Default value if not set\n required: If True, raises error when not set\n\n Returns:\n Environment variable value\n\n Raises:\n ConfigError: If required=True and variable not set\n \"\"\"\n value = os.getenv(key, default)\n if required and value is None:\n raise ConfigError(f\"Required environment variable '{key}' is not set\")\n return value\n```\n\n### Usage Example\n\n```python\n# main.py\nfrom config import config\n\ndef main():\n print(f\"Starting {config.app_name} in {config.app_env} mode\")\n print(f\"Debug mode: {config.debug}\")\n\n if config.anthropic_api_key:\n print(\"✓ Anthropic API key loaded\")\n\n # Use config throughout your app\n if config.debug:\n print(f\"Database: {config.database_url}\")\n\nif __name__ == \"__main__\":\n main()\n```\n\n## Pydantic-Based Configuration\n\n### Type-Safe Config with Validation\n\n```python\n# config_pydantic.py\nfrom pydantic import BaseSettings, Field, validator\n\n\nclass Settings(BaseSettings):\n \"\"\"Type-safe application settings.\"\"\"\n\n # Application\n app_name: str = Field(default='MyApp', env='APP_NAME')\n app_env: str = Field(default='development', env='APP_ENV')\n debug: bool = Field(default=False, env='DEBUG')\n\n # Database\n database_url: str = Field(..., env='DATABASE_URL') # Required\n database_pool_size: int = Field(default=5, env='DATABASE_POOL_SIZE')\n\n # API Keys\n anthropic_api_key: str = Field(default='', env='ANTHROPIC_API_KEY')\n\n @validator('app_env')\n def validate_env(cls, v):\n allowed = ['development', 'staging', 'production']\n if v not in allowed:\n raise ValueError(f'app_env must be one of {allowed}')\n return v\n\n @validator('database_pool_size')\n def validate_pool_size(cls, v):\n if v \u003c 1 or v > 100:\n raise ValueError('database_pool_size must be between 1 and 100')\n return v\n\n class Config:\n env_file = '.env'\n env_file_encoding = 'utf-8'\n case_sensitive = False\n\n\n# Usage\nsettings = Settings()\nprint(settings.app_name)\nprint(settings.database_url)\n```\n\n## JSON Secrets Loading\n\n### Secrets Loader Implementation\n\n```python\n# secrets_loader.py\nimport json\nimport os\nfrom pathlib import Path\n\n\ndef load_secrets(secrets_file: str = 'secrets.json') -> dict:\n \"\"\"\n Load secrets from JSON file with fallback to environment variables.\n\n Args:\n secrets_file: Path to secrets JSON file\n\n Returns:\n Dictionary of secrets\n \"\"\"\n secrets_path = Path(secrets_file)\n\n # Try loading from JSON file\n if secrets_path.exists():\n try:\n with open(secrets_path, 'r') as f:\n secrets = json.load(f)\n print(f\"✓ Loaded secrets from {secrets_file}\")\n return secrets\n except json.JSONDecodeError as e:\n print(f\"⚠ Error parsing {secrets_file}: {e}\")\n print(\" Falling back to environment variables\")\n else:\n print(f\"ℹ {secrets_file} not found, using environment variables\")\n\n # Fallback to environment variables\n return {\n 'anthropic_api_key': os.getenv('ANTHROPIC_API_KEY', ''),\n 'openai_api_key': os.getenv('OPENAI_API_KEY', ''),\n 'database_password': os.getenv('DATABASE_PASSWORD', ''),\n }\n\n\n# Usage\nsecrets = load_secrets()\napi_key = secrets.get('anthropic_api_key', os.getenv('ANTHROPIC_API_KEY', ''))\n```\n\n## Helper Functions\n\n### Environment Variable Getter\n\n```python\ndef get_env(key: str, default: str = None, required: bool = False) -> str:\n \"\"\"\n Get environment variable with validation.\n\n Args:\n key: Environment variable name\n default: Default value if not set\n required: If True, raises error when not set\n\n Returns:\n Environment variable value\n \"\"\"\n value = os.getenv(key, default)\n if required and value is None:\n raise ConfigError(f\"Required environment variable '{key}' is not set\")\n return value\n```\n\n### Boolean Converter\n\n```python\ndef str_to_bool(value: str) -> bool:\n \"\"\"Convert string to boolean.\"\"\"\n return value.lower() in ('true', '1', 'yes', 'on')\n```\n\n### Integer with Validation\n\n```python\ndef get_int_env(key: str, default: int, min_val: int = None, max_val: int = None) -> int:\n \"\"\"Get integer environment variable with range validation.\"\"\"\n value = int(os.getenv(key, str(default)))\n if min_val is not None and value \u003c min_val:\n raise ValueError(f\"{key} must be >= {min_val}\")\n if max_val is not None and value > max_val:\n raise ValueError(f\"{key} must be \u003c= {max_val}\")\n return value\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8129,"content_sha256":"c0b6470c32568439d13628a30e4faf1b45279cabac45c765d9fa0d2878946d78"},{"filename":"references/testing-guide.md","content":"# Configuration Testing Guide\n\n## pytest with Environment Variables\n\n### Test Configuration Setup\n\nCreate `conftest.py`:\n\n```python\nimport pytest\nimport os\nfrom pathlib import Path\n\n\[email protected]\ndef test_env():\n \"\"\"Set up test environment variables.\"\"\"\n original = os.environ.copy()\n\n # Set test values\n os.environ['APP_ENV'] = 'testing'\n os.environ['DEBUG'] = 'true'\n os.environ['DATABASE_URL'] = 'sqlite:///:memory:'\n os.environ['APP_NAME'] = 'TestApp'\n\n yield\n\n # Restore original environment\n os.environ.clear()\n os.environ.update(original)\n\n\[email protected]\ndef config(test_env):\n \"\"\"Provide clean config for each test.\"\"\"\n from config import Config\n return Config(env='testing')\n\n\[email protected]\ndef temp_env_file(tmp_path):\n \"\"\"Create temporary .env file for testing.\"\"\"\n env_file = tmp_path / \".env\"\n env_content = \"\"\"\nAPP_NAME=TestApp\nAPP_ENV=testing\nDEBUG=true\nDATABASE_URL=sqlite:///:memory:\n\"\"\"\n env_file.write_text(env_content)\n return env_file\n```\n\n### Basic Configuration Tests\n\n`test_config.py`:\n\n```python\nimport pytest\nimport os\nfrom config import Config, ConfigError\n\n\ndef test_config_loading(config):\n \"\"\"Test configuration loads correctly.\"\"\"\n assert config.app_env == 'testing'\n assert config.debug is True\n assert config.app_name == 'TestApp'\n\n\ndef test_missing_required_var():\n \"\"\"Test error raised for missing required variables.\"\"\"\n # Remove required var\n old_val = os.environ.pop('APP_NAME', None)\n\n try:\n with pytest.raises(ConfigError):\n Config()\n finally:\n if old_val:\n os.environ['APP_NAME'] = old_val\n\n\ndef test_environment_specific_loading(tmp_path):\n \"\"\"Test environment-specific .env file loading.\"\"\"\n # Create development env file\n dev_env = tmp_path / \".env.development\"\n dev_env.write_text(\"APP_NAME=DevApp\\nDEBUG=true\")\n\n # Change to temp directory\n os.chdir(tmp_path)\n\n config = Config(env='development')\n assert config.app_name == 'DevApp'\n assert config.debug is True\n\n\ndef test_boolean_parsing(config):\n \"\"\"Test boolean environment variable parsing.\"\"\"\n os.environ['DEBUG'] = 'true'\n assert config.debug is True\n\n os.environ['DEBUG'] = 'false'\n config._load_env_file()\n assert config.debug is False\n\n os.environ['DEBUG'] = '1'\n config._load_env_file()\n assert config.debug is True\n\n\ndef test_optional_values(config):\n \"\"\"Test optional configuration values.\"\"\"\n # Remove optional key\n os.environ.pop('OPENAI_API_KEY', None)\n\n # Should not raise error\n assert config.openai_api_key is None\n\n\ndef test_default_values(config):\n \"\"\"Test default values when env vars not set.\"\"\"\n os.environ.pop('LOG_LEVEL', None)\n assert config.log_level == 'INFO'\n```\n\n### Integration Tests\n\n```python\ndef test_full_application_startup(tmp_path):\n \"\"\"Test complete application configuration flow.\"\"\"\n # Create .env file\n env_file = tmp_path / \".env\"\n env_file.write_text(\"\"\"\nAPP_NAME=IntegrationTest\nAPP_ENV=testing\nDEBUG=true\nDATABASE_URL=postgresql://localhost/test_db\nANTHROPIC_API_KEY=sk-test-key\nSECRET_KEY=test-secret-key\n\"\"\")\n\n os.chdir(tmp_path)\n\n # Initialize config\n config = Config()\n\n # Verify all settings\n assert config.app_name == 'IntegrationTest'\n assert config.database_url == 'postgresql://localhost/test_db'\n assert config.anthropic_api_key == 'sk-test-key'\n assert config.secret_key == 'test-secret-key'\n\n\ndef test_multi_environment_priority(tmp_path):\n \"\"\"Test that environment-specific files override base .env.\"\"\"\n # Create base .env\n base_env = tmp_path / \".env\"\n base_env.write_text(\"APP_NAME=BaseApp\\nDEBUG=false\")\n\n # Create environment-specific .env\n prod_env = tmp_path / \".env.production\"\n prod_env.write_text(\"APP_NAME=ProdApp\\nDEBUG=false\")\n\n os.chdir(tmp_path)\n\n config = Config(env='production')\n\n # Should use production-specific value\n assert config.app_name == 'ProdApp'\n```\n\n### Error Handling Tests\n\n```python\ndef test_missing_env_file_handling():\n \"\"\"Test graceful handling of missing .env files.\"\"\"\n # Should not crash, just use defaults or env vars\n os.environ['APP_NAME'] = 'TestApp'\n os.environ['APP_ENV'] = 'testing'\n\n config = Config()\n assert config.app_name == 'TestApp'\n\n\ndef test_malformed_env_file(tmp_path):\n \"\"\"Test handling of malformed .env files.\"\"\"\n env_file = tmp_path / \".env\"\n env_file.write_text(\"INVALID LINE WITHOUT EQUALS\")\n\n os.chdir(tmp_path)\n\n # Should still load, python-dotenv is forgiving\n config = Config()\n\n\ndef test_production_without_secret_key():\n \"\"\"Test that production requires SECRET_KEY.\"\"\"\n os.environ['APP_ENV'] = 'production'\n os.environ['DEBUG'] = 'false'\n os.environ.pop('SECRET_KEY', None)\n\n config = Config(env='production')\n\n with pytest.raises(ConfigError):\n _ = config.secret_key\n```\n\n## Testing Secrets Loading\n\n### JSON Secrets Tests\n\n```python\nfrom secrets_loader import load_secrets\n\n\ndef test_secrets_from_json(tmp_path):\n \"\"\"Test loading secrets from JSON file.\"\"\"\n secrets_file = tmp_path / \"secrets.json\"\n secrets_file.write_text(json.dumps({\n \"anthropic_api_key\": \"sk-test-123\",\n \"openai_api_key\": \"sk-openai-456\"\n }))\n\n os.chdir(tmp_path)\n\n secrets = load_secrets()\n assert secrets['anthropic_api_key'] == 'sk-test-123'\n assert secrets['openai_api_key'] == 'sk-openai-456'\n\n\ndef test_secrets_fallback_to_env():\n \"\"\"Test fallback to environment variables.\"\"\"\n os.environ['ANTHROPIC_API_KEY'] = 'env-key-123'\n\n # No secrets.json exists\n secrets = load_secrets('nonexistent.json')\n\n assert secrets['anthropic_api_key'] == 'env-key-123'\n\n\ndef test_malformed_json_secrets(tmp_path):\n \"\"\"Test handling of malformed JSON secrets file.\"\"\"\n secrets_file = tmp_path / \"secrets.json\"\n secrets_file.write_text(\"{ invalid json }\")\n\n os.chdir(tmp_path)\n\n # Should fallback to environment variables\n os.environ['ANTHROPIC_API_KEY'] = 'fallback-key'\n secrets = load_secrets()\n\n assert secrets['anthropic_api_key'] == 'fallback-key'\n```\n\n## Mocking Environment Variables\n\n### Using pytest-env\n\nInstall: `uv add --dev pytest-env`\n\nConfigure in `pytest.ini`:\n\n```ini\n[pytest]\nenv =\n APP_ENV=testing\n DEBUG=true\n DATABASE_URL=sqlite:///:memory:\n```\n\n### Using unittest.mock\n\n```python\nfrom unittest.mock import patch\n\n\ndef test_config_with_mock():\n \"\"\"Test configuration with mocked environment.\"\"\"\n with patch.dict(os.environ, {\n 'APP_NAME': 'MockedApp',\n 'APP_ENV': 'testing',\n 'DEBUG': 'true'\n }):\n config = Config()\n assert config.app_name == 'MockedApp'\n\n\ndef test_api_key_present():\n \"\"\"Test behavior when API key is present.\"\"\"\n with patch.dict(os.environ, {'ANTHROPIC_API_KEY': 'test-key'}):\n config = Config()\n assert config.anthropic_api_key == 'test-key'\n\n\ndef test_api_key_absent():\n \"\"\"Test behavior when API key is absent.\"\"\"\n with patch.dict(os.environ, {}, clear=True):\n os.environ['APP_NAME'] = 'TestApp'\n os.environ['APP_ENV'] = 'testing'\n config = Config()\n assert config.anthropic_api_key is None\n```\n\n## Validation Testing\n\n### Pydantic Config Tests\n\n```python\nfrom pydantic import ValidationError\nfrom config_pydantic import Settings\n\n\ndef test_valid_settings():\n \"\"\"Test valid settings creation.\"\"\"\n with patch.dict(os.environ, {\n 'APP_NAME': 'TestApp',\n 'DATABASE_URL': 'postgresql://localhost/db'\n }):\n settings = Settings()\n assert settings.app_name == 'TestApp'\n\n\ndef test_invalid_env_value():\n \"\"\"Test validation error for invalid environment.\"\"\"\n with patch.dict(os.environ, {\n 'APP_ENV': 'invalid',\n 'DATABASE_URL': 'postgresql://localhost/db'\n }):\n with pytest.raises(ValidationError):\n Settings()\n\n\ndef test_missing_required_field():\n \"\"\"Test validation error for missing required field.\"\"\"\n with patch.dict(os.environ, {'APP_NAME': 'TestApp'}):\n # Missing required DATABASE_URL\n with pytest.raises(ValidationError):\n Settings()\n\n\ndef test_type_coercion():\n \"\"\"Test automatic type coercion.\"\"\"\n with patch.dict(os.environ, {\n 'DEBUG': 'true',\n 'DATABASE_POOL_SIZE': '10',\n 'DATABASE_URL': 'postgresql://localhost/db'\n }):\n settings = Settings()\n assert settings.debug is True\n assert settings.database_pool_size == 10\n assert isinstance(settings.database_pool_size, int)\n```\n\n## Coverage and Quality\n\n### Test Coverage Goals\n\nAim for:\n- 95%+ coverage on configuration loading\n- 100% coverage on validation logic\n- 100% coverage on error handling\n\nRun coverage:\n\n```bash\nuv run pytest --cov=config --cov-report=html\n```\n\n### Property-Based Testing\n\nUse Hypothesis for property-based tests:\n\n```bash\nuv add --dev hypothesis\n```\n\n```python\nfrom hypothesis import given, strategies as st\n\n\n@given(st.text(min_size=1))\ndef test_any_app_name_accepted(app_name):\n \"\"\"Test that any non-empty string is valid for APP_NAME.\"\"\"\n with patch.dict(os.environ, {\n 'APP_NAME': app_name,\n 'APP_ENV': 'testing'\n }):\n config = Config()\n assert config.app_name == app_name\n\n\n@given(st.booleans())\ndef test_debug_boolean_handling(debug_value):\n \"\"\"Test debug flag handles all boolean values.\"\"\"\n with patch.dict(os.environ, {\n 'DEBUG': str(debug_value).lower(),\n 'APP_NAME': 'TestApp',\n 'APP_ENV': 'testing'\n }):\n config = Config()\n assert isinstance(config.debug, bool)\n```\n\n## CI/CD Testing\n\n### GitHub Actions Example\n\n```yaml\nname: Test Configuration\n\non: [push, pull_request]\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n\n - name: Install UV\n run: curl -LsSf https://astral.sh/uv/install.sh | sh\n\n - name: Install dependencies\n run: uv sync\n\n - name: Run tests\n env:\n APP_ENV: testing\n APP_NAME: CI-Test\n run: uv run pytest\n\n - name: Check coverage\n run: uv run pytest --cov=config --cov-fail-under=90\n```\n\n### Testing in Docker\n\n```dockerfile\nFROM python:3.11-slim\n\nRUN pip install uv\nWORKDIR /app\nCOPY . .\nRUN uv sync --dev\n\n# Set test environment\nENV APP_ENV=testing\nENV APP_NAME=DockerTest\n\nCMD [\"uv\", \"run\", \"pytest\"]\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10426,"content_sha256":"69744ec15b5bccf23cb6d04eee7e86a39bc34d50cf4363be966cea9efc02253f"},{"filename":"references/troubleshooting.md","content":"# Configuration Troubleshooting Guide\n\n## Common Issues\n\n### Issue 1: Environment Variables Not Loading\n\n**Symptoms:**\n- Application shows default values instead of .env values\n- `os.getenv()` returns `None` for expected variables\n- Configuration appears empty\n\n**Diagnosis:**\n\n```python\n# Debug: Print loaded variables\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv(verbose=True) # Shows what's being loaded\nprint(f\"APP_NAME: {os.getenv('APP_NAME')}\")\nprint(f\"Current directory: {os.getcwd()}\")\nprint(f\".env exists: {Path('.env').exists()}\")\n```\n\n**Solutions:**\n\n1. **Check .env file location**\n ```bash\n # Verify .env is in current directory\n ls -la .env\n\n # Or specify path explicitly\n python\n from dotenv import load_dotenv\n load_dotenv('/absolute/path/to/.env')\n ```\n\n2. **Verify file permissions**\n ```bash\n # Make sure file is readable\n chmod 644 .env\n ```\n\n3. **Check for BOM or encoding issues**\n ```python\n # Re-save .env file as UTF-8 without BOM\n content = Path('.env').read_text(encoding='utf-8-sig')\n Path('.env').write_text(content, encoding='utf-8')\n ```\n\n4. **Verify syntax**\n ```bash\n # Check for syntax errors\n cat .env | grep -v '^#' | grep -v '^

Environment Configuration Skill Overview This skill provides comprehensive guidance for managing environment configurations, secrets, and environment variables in Python projects using UV (the modern Python package and project manager). It covers secure configuration patterns, multi-environment setups, .env file management, and secrets handling with encryption support. Environment configuration is critical for separating configuration from code (12-factor app principles), managing secrets securely across environments, preventing credential leaks, and supporting team collaboration. When to Use…

\n\n # Should be: KEY=value (no spaces around =)\n # ✓ APP_NAME=MyApp\n # ✗ APP_NAME = MyApp\n ```\n\n### Issue 2: Wrong Environment Loaded\n\n**Symptoms:**\n- Production settings in development\n- Development debug mode in production\n- Unexpected configuration values\n\n**Diagnosis:**\n\n```python\n# Check which environment is active\nimport os\nprint(f\"APP_ENV: {os.getenv('APP_ENV')}\")\nprint(f\"Current env files:\")\nfrom pathlib import Path\nfor env_file in Path('.').glob('.env*'):\n print(f\" - {env_file}\")\n```\n\n**Solutions:**\n\n1. **Explicitly set environment**\n ```bash\n # Set before running\n export APP_ENV=development\n python main.py\n\n # Or inline\n APP_ENV=production python main.py\n ```\n\n2. **Check environment loading order**\n ```python\n # Verify load order in config\n from config import config\n print(f\"Using environment: {config.app_env}\")\n\n # Force specific environment\n config = Config(env='development')\n ```\n\n3. **Use environment-specific commands**\n ```bash\n # Add to package.json or Makefile\n uv run python main.py --env=development\n uv run python main.py --env=production\n ```\n\n### Issue 3: UV sync Fails\n\n**Symptoms:**\n- `uv sync` command errors\n- Dependencies not installing\n- Virtual environment issues\n\n**Solutions:**\n\n1. **Clear UV cache**\n ```bash\n # Clean cache\n uv cache clean\n\n # Remove and recreate venv\n rm -rf .venv\n uv venv\n uv sync\n ```\n\n2. **Check Python version**\n ```bash\n # Verify Python version\n python --version\n\n # UV requires Python 3.8+\n uv python pin 3.11\n ```\n\n3. **Update UV**\n ```bash\n # Get latest UV\n pip install --upgrade uv\n\n # Or reinstall\n curl -LsSf https://astral.sh/uv/install.sh | sh\n ```\n\n4. **Check network/proxy**\n ```bash\n # Test network\n curl -I https://pypi.org\n\n # Set proxy if needed\n export HTTP_PROXY=http://proxy:port\n export HTTPS_PROXY=http://proxy:port\n ```\n\n### Issue 4: Import Errors\n\n**Symptoms:**\n- `ModuleNotFoundError: No module named 'dotenv'`\n- `ImportError: cannot import name 'load_dotenv'`\n\n**Solutions:**\n\n1. **Verify dependencies installed**\n ```bash\n # Check if python-dotenv is installed\n uv pip list | grep dotenv\n\n # Install if missing\n uv add python-dotenv\n ```\n\n2. **Check virtual environment activation**\n ```bash\n # Activate venv\n source .venv/bin/activate # macOS/Linux\n .venv\\Scripts\\activate # Windows\n\n # Verify correct Python\n which python\n ```\n\n3. **Reinstall dependencies**\n ```bash\n # Force reinstall\n uv sync --reinstall\n ```\n\n### Issue 5: Secrets Not Decrypting\n\n**Symptoms:**\n- Encryption/decryption fails\n- Invalid key errors\n- Corrupted secrets file\n\n**Solutions:**\n\n1. **Verify encryption key**\n ```python\n # Check key format\n import base64\n key = os.getenv('ENCRYPTION_KEY')\n try:\n decoded = base64.b64decode(key)\n print(f\"Key length: {len(decoded)} bytes\")\n except Exception as e:\n print(f\"Invalid key: {e}\")\n ```\n\n2. **Re-encrypt secrets**\n ```bash\n # Backup current encrypted file\n cp secrets.encrypted secrets.encrypted.bak\n\n # Re-encrypt from source\n python scripts/env_helper.py encrypt secrets.json secrets.encrypted\n ```\n\n3. **Check file corruption**\n ```bash\n # Verify file integrity\n file secrets.encrypted\n hexdump -C secrets.encrypted | head\n ```\n\n## Environment-Specific Issues\n\n### Development Environment\n\n**Issue: Local services not connecting**\n\n```bash\n# Check if services are running\ndocker ps # For containerized services\nlsof -i :5432 # PostgreSQL\nlsof -i :6379 # Redis\n\n# Start services\ndocker-compose up -d\n```\n\n**Issue: Debug output too verbose**\n\n```bash\n# Adjust log level\nLOG_LEVEL=WARNING python main.py\n```\n\n### Staging Environment\n\n**Issue: API keys not working**\n\n```bash\n# Verify API keys are for staging environment\ncurl -H \"Authorization: Bearer $ANTHROPIC_API_KEY\" \\\n https://api.anthropic.com/v1/endpoint\n\n# Check rate limits\n# Use staging-specific keys\n```\n\n**Issue: Database connection timeout**\n\n```python\n# Increase connection timeout\nDATABASE_URL=postgresql://host/db?connect_timeout=30\nDATABASE_POOL_SIZE=10 # Increase pool\n```\n\n### Production Environment\n\n**Issue: Application won't start**\n\n```bash\n# Check all required vars set\npython -c \"\nfrom config import Config\ntry:\n config = Config(env='production')\n print('✓ Configuration valid')\nexcept Exception as e:\n print(f'✗ Configuration error: {e}')\n\"\n```\n\n**Issue: Secrets not loading**\n\n```bash\n# Verify secrets file permissions\nls -l secrets.json\n# Should be: -rw------- (600)\n\n# Fix permissions\nchmod 600 secrets.json\nchown app:app secrets.json\n```\n\n## Performance Issues\n\n### Issue: Slow Configuration Loading\n\n**Solutions:**\n\n1. **Cache configuration**\n ```python\n # Use singleton pattern\n _config = None\n\n def get_config():\n global _config\n if _config is None:\n _config = Config()\n return _config\n ```\n\n2. **Lazy load optional config**\n ```python\n class Config:\n @property\n def expensive_resource(self):\n if not hasattr(self, '_expensive_resource'):\n self._expensive_resource = load_expensive_config()\n return self._expensive_resource\n ```\n\n3. **Reduce validation overhead**\n ```python\n # Validate once at startup\n config = Config()\n config._validate_required() # Only once\n\n # Skip validation in subsequent uses\n ```\n\n### Issue: Too Many Environment Files\n\n**Solution: Consolidate**\n\n```bash\n# Before (slow)\n.env\n.env.local\n.env.development\n.env.development.local\n.env.staging\n.env.production\n\n# After (faster)\n.env # Shared defaults\n.env.development # Dev-specific\n.env.production # Prod-specific\n```\n\n## Security Issues\n\n### Issue: Secrets in Version Control\n\n**Detection:**\n\n```bash\n# Check git history for secrets\ngit log --all --full-history --source -- .env\ngit log --all -p | grep -i \"api_key\"\n\n# Use git-secrets\ngit secrets --scan\n```\n\n**Remediation:**\n\n```bash\n# Remove from git history\ngit filter-branch --force --index-filter \\\n \"git rm --cached --ignore-unmatch .env\" \\\n --prune-empty --tag-name-filter cat -- --all\n\n# Or use BFG Repo-Cleaner\nbfg --delete-files .env\ngit reflog expire --expire=now --all\ngit gc --prune=now --aggressive\n```\n\n**Prevention:**\n\n```bash\n# Add to .gitignore immediately\necho \".env\" >> .gitignore\necho \"secrets.json\" >> .gitignore\ngit add .gitignore\ngit commit -m \"Add .env to gitignore\"\n\n# Use pre-commit hooks\n# .git/hooks/pre-commit\n#!/bin/bash\nif git diff --cached --name-only | grep -q \"\\.env$\"; then\n echo \"Error: Attempting to commit .env file\"\n exit 1\nfi\n```\n\n### Issue: API Keys Exposed in Logs\n\n**Detection:**\n\n```bash\n# Search logs for keys\ngrep -r \"sk-ant-\" logs/\ngrep -r \"api.key\" logs/\n```\n\n**Prevention:**\n\n```python\nimport re\n\nclass SecureLogger:\n \"\"\"Logger that redacts sensitive values.\"\"\"\n\n PATTERNS = [\n r'sk-ant-[a-zA-Z0-9-]+', # Anthropic keys\n r'sk-[a-zA-Z0-9]{32,}', # OpenAI keys\n r'password=\\S+', # Passwords\n ]\n\n @classmethod\n def redact(cls, message: str) -> str:\n \"\"\"Redact sensitive patterns from message.\"\"\"\n for pattern in cls.PATTERNS:\n message = re.sub(pattern, '[REDACTED]', message)\n return message\n\n# Usage\nlogger.info(SecureLogger.redact(f\"API key: {api_key}\"))\n```\n\n## Validation Issues\n\n### Issue: Type Errors\n\n```python\n# Problem: String instead of int\nPORT = os.getenv('PORT') # Returns string \"8000\"\n\n# Solution: Convert types\nPORT = int(os.getenv('PORT', '8000'))\n\n# Or use Pydantic for automatic conversion\nfrom pydantic import BaseSettings\n\nclass Settings(BaseSettings):\n port: int = 8000 # Automatically converts string to int\n```\n\n### Issue: Boolean Confusion\n\n```python\n# Problem: All strings are truthy\nDEBUG = os.getenv('DEBUG') # \"false\" is truthy!\n\n# Solution: Explicit conversion\ndef str_to_bool(value: str) -> bool:\n return value.lower() in ('true', '1', 'yes', 'on')\n\nDEBUG = str_to_bool(os.getenv('DEBUG', 'false'))\n```\n\n## Docker Issues\n\n### Issue: Environment Variables Not Available in Container\n\n**Solutions:**\n\n1. **Pass variables explicitly**\n ```bash\n docker run -e APP_ENV=production \\\n -e DATABASE_URL=$DATABASE_URL \\\n myapp\n ```\n\n2. **Use env file**\n ```bash\n docker run --env-file .env.production myapp\n ```\n\n3. **Check Dockerfile**\n ```dockerfile\n # Don't hardcode ENV in Dockerfile\n # ✗ Bad\n ENV APP_ENV=development\n\n # ✓ Good - use runtime injection\n # (no ENV declaration)\n ```\n\n### Issue: Different Behavior in Container vs Local\n\n**Solutions:**\n\n1. **Match environments**\n ```bash\n # Use same Python version\n docker run python:3.11-slim python --version\n python --version # Should match\n ```\n\n2. **Check working directory**\n ```dockerfile\n WORKDIR /app\n # .env should be in /app/.env\n ```\n\n3. **Verify file copying**\n ```dockerfile\n # Make sure .env.template is copied\n COPY .env.template .\n # But don't copy .env (use runtime vars)\n ```\n\n## Getting Help\n\n### Enable Verbose Logging\n\n```python\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\n\nfrom dotenv import load_dotenv\nload_dotenv(verbose=True) # Shows loading process\n```\n\n### Create Minimal Reproduction\n\n```python\n# minimal_test.py\nimport os\nfrom pathlib import Path\nfrom dotenv import load_dotenv\n\nprint(f\"Current directory: {os.getcwd()}\")\nprint(f\".env exists: {Path('.env').exists()}\")\n\nload_dotenv(verbose=True)\n\nprint(f\"APP_NAME: {os.getenv('APP_NAME')}\")\nprint(f\"All env vars starting with APP_:\")\nfor key, value in os.environ.items():\n if key.startswith('APP_'):\n print(f\" {key}={value}\")\n```\n\n### Check Versions\n\n```bash\n# Print diagnostic info\npython --version\nuv --version\npip list | grep -E \"(dotenv|pydantic|cryptography)\"\n\n# System info\nuname -a # Linux/macOS\nsysteminfo # Windows\n```\n\n### Report Issues\n\nWhen reporting configuration issues, include:\n\n1. Python version: `python --version`\n2. UV version: `uv --version`\n3. OS: `uname -a`\n4. .env file structure (with secrets redacted)\n5. Error messages (full stack trace)\n6. Steps to reproduce\n7. Expected vs actual behavior\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11396,"content_sha256":"44b8d36b48604a330c5e567d91afd29740199e2d94719c4a83672bf99b124297"},{"filename":"scripts/env_helper.py","content":"#!/usr/bin/env python3\n\"\"\"\nEnvironment Configuration Helper Script\n\nUtilities for managing .env files, validating configuration,\nand handling secrets encryption.\n\nDependencies:\n uv add python-dotenv cryptography\n\nAuthor: Claude Code\nVersion: 1.0.0\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport base64\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, Tuple\nfrom datetime import datetime\nimport argparse\n\n\ndef load_env_file(file_path: str = '.env') -> Dict[str, str]:\n \"\"\"\n Parse .env file into dictionary.\n\n Args:\n file_path: Path to .env file\n\n Returns:\n Dictionary of environment variables\n\n Example:\n >>> vars = load_env_file('.env.development')\n >>> print(vars.get('APP_NAME'))\n \"\"\"\n env_vars = {}\n path = Path(file_path)\n\n if not path.exists():\n print(f\"⚠ File not found: {file_path}\")\n return env_vars\n\n with open(path, 'r') as f:\n for line_num, line in enumerate(f, 1):\n line = line.strip()\n\n # Skip empty lines and comments\n if not line or line.startswith('#'):\n continue\n\n # Parse KEY=VALUE\n if '=' in line:\n key, value = line.split('=', 1)\n key = key.strip()\n value = value.strip()\n\n # Remove quotes if present\n if value.startswith('\"') and value.endswith('\"'):\n value = value[1:-1]\n elif value.startswith(\"'\") and value.endswith(\"'\"):\n value = value[1:-1]\n\n env_vars[key] = value\n else:\n print(f\"⚠ Skipping invalid line {line_num}: {line}\")\n\n return env_vars\n\n\ndef validate_env_file(file_path: str, required_vars: List[str] = None) -> Tuple[bool, List[str]]:\n \"\"\"\n Validate .env file has required variables.\n\n Args:\n file_path: Path to .env file\n required_vars: List of required variable names\n\n Returns:\n Tuple of (is_valid, missing_vars)\n\n Example:\n >>> valid, missing = validate_env_file('.env', ['APP_NAME', 'DATABASE_URL'])\n >>> if not valid:\n ... print(f\"Missing: {missing}\")\n \"\"\"\n if required_vars is None:\n required_vars = []\n\n env_vars = load_env_file(file_path)\n missing = [var for var in required_vars if var not in env_vars or not env_vars[var]]\n\n return (len(missing) == 0, missing)\n\n\ndef check_env_security(file_path: str = '.env') -> List[str]:\n \"\"\"\n Check .env file for potential security issues.\n\n Args:\n file_path: Path to .env file\n\n Returns:\n List of security warnings\n\n Example:\n >>> warnings = check_env_security('.env')\n >>> for warning in warnings:\n ... print(f\"⚠ {warning}\")\n \"\"\"\n warnings = []\n env_vars = load_env_file(file_path)\n\n # Check for common insecure patterns\n insecure_patterns = [\n 'xxx',\n 'example',\n 'test123',\n 'password123',\n 'changeme',\n 'your-key-here',\n 'replace-this',\n ]\n\n for key, value in env_vars.items():\n value_lower = value.lower()\n\n # Check for placeholder values\n if any(pattern in value_lower for pattern in insecure_patterns):\n warnings.append(f\"{key} appears to contain a placeholder value: {value[:20]}...\")\n\n # Check for empty sensitive keys\n if any(sensitive in key.upper() for sensitive in ['KEY', 'SECRET', 'PASSWORD', 'TOKEN']):\n if not value:\n warnings.append(f\"{key} is empty but appears to be sensitive\")\n\n # Check for short passwords/secrets\n if 'PASSWORD' in key.upper() or 'SECRET' in key.upper():\n if len(value) \u003c 12:\n warnings.append(f\"{key} seems too short (less than 12 characters)\")\n\n return warnings\n\n\ndef compare_env_files(file1: str, file2: str) -> Dict[str, Dict[str, Optional[str]]]:\n \"\"\"\n Compare two .env files and show differences.\n\n Args:\n file1: Path to first .env file\n file2: Path to second .env file\n\n Returns:\n Dictionary with 'only_in_1', 'only_in_2', 'different'\n\n Example:\n >>> diff = compare_env_files('.env.development', '.env.production')\n >>> print(\"Only in dev:\", diff['only_in_1'])\n \"\"\"\n env1 = load_env_file(file1)\n env2 = load_env_file(file2)\n\n all_keys = set(env1.keys()) | set(env2.keys())\n\n only_in_1 = {k: env1[k] for k in env1 if k not in env2}\n only_in_2 = {k: env2[k] for k in env2 if k not in env1}\n different = {\n k: {'file1': env1[k], 'file2': env2[k]}\n for k in all_keys\n if k in env1 and k in env2 and env1[k] != env2[k]\n }\n\n return {\n 'only_in_1': only_in_1,\n 'only_in_2': only_in_2,\n 'different': different,\n }\n\n\ndef generate_env_template(source_file: str, output_file: str, mask_values: bool = True):\n \"\"\"\n Generate .env.template from existing .env file.\n\n Args:\n source_file: Source .env file\n output_file: Output template file\n mask_values: If True, replace values with placeholders\n\n Example:\n >>> generate_env_template('.env', '.env.template')\n \"\"\"\n env_vars = load_env_file(source_file)\n\n with open(output_file, 'w') as f:\n f.write(f\"# Environment Configuration Template\\n\")\n f.write(f\"# Generated from {source_file} on {datetime.now().strftime('%Y-%m-%d')}\\n\")\n f.write(f\"# Copy to .env and fill in actual values\\n\\n\")\n\n for key, value in sorted(env_vars.items()):\n if mask_values:\n # Mask sensitive values\n if any(s in key.upper() for s in ['KEY', 'SECRET', 'PASSWORD', 'TOKEN']):\n if 'URL' in key.upper():\n # Keep URL structure but mask credentials\n value = 'your-service-url-here'\n else:\n value = 'your-secret-key-here'\n elif 'URL' in key.upper() or 'HOST' in key.upper():\n value = 'your-service-url-here'\n elif value.lower() in ['true', 'false']:\n # Keep boolean values\n pass\n elif value.isdigit():\n # Keep numeric values\n pass\n\n f.write(f\"{key}={value}\\n\")\n\n print(f\"✓ Template generated: {output_file}\")\n\n\ndef encrypt_secrets(input_file: str, output_file: str, password: str):\n \"\"\"\n Encrypt secrets file using Fernet encryption.\n\n Args:\n input_file: Plain text secrets file\n output_file: Encrypted output file\n password: Encryption password\n\n Requires: uv add cryptography\n\n Example:\n >>> encrypt_secrets('secrets.json', 'secrets.encrypted', 'my-password')\n \"\"\"\n try:\n from cryptography.fernet import Fernet\n from cryptography.hazmat.primitives import hashes\n from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2\n from cryptography.hazmat.backends import default_backend\n except ImportError:\n print(\"❌ cryptography not installed. Run: uv add cryptography\")\n sys.exit(1)\n\n # Read input file\n with open(input_file, 'rb') as f:\n data = f.read()\n\n # Derive key from password\n salt = os.urandom(16)\n kdf = PBKDF2(\n algorithm=hashes.SHA256(),\n length=32,\n salt=salt,\n iterations=100000,\n backend=default_backend()\n )\n key = base64.urlsafe_b64encode(kdf.derive(password.encode()))\n\n # Encrypt\n fernet = Fernet(key)\n encrypted = fernet.encrypt(data)\n\n # Write encrypted file with salt\n with open(output_file, 'wb') as f:\n f.write(salt + encrypted)\n\n print(f\"✓ Encrypted {input_file} -> {output_file}\")\n\n\ndef decrypt_secrets(input_file: str, password: str) -> dict:\n \"\"\"\n Decrypt encrypted secrets file.\n\n Args:\n input_file: Encrypted secrets file\n password: Decryption password\n\n Returns:\n Dictionary of decrypted secrets\n\n Example:\n >>> secrets = decrypt_secrets('secrets.encrypted', 'my-password')\n >>> print(secrets.get('api_key'))\n \"\"\"\n try:\n from cryptography.fernet import Fernet\n from cryptography.hazmat.primitives import hashes\n from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2\n from cryptography.hazmat.backends import default_backend\n except ImportError:\n print(\"❌ cryptography not installed. Run: uv add cryptography\")\n sys.exit(1)\n\n # Read encrypted file\n with open(input_file, 'rb') as f:\n data = f.read()\n\n # Extract salt and encrypted data\n salt = data[:16]\n encrypted = data[16:]\n\n # Derive key from password\n kdf = PBKDF2(\n algorithm=hashes.SHA256(),\n length=32,\n salt=salt,\n iterations=100000,\n backend=default_backend()\n )\n key = base64.urlsafe_b64encode(kdf.derive(password.encode()))\n\n # Decrypt\n fernet = Fernet(key)\n try:\n decrypted = fernet.decrypt(encrypted)\n except Exception as e:\n print(f\"❌ Decryption failed: {e}\")\n print(\" Check password is correct\")\n sys.exit(1)\n\n # Parse JSON\n return json.loads(decrypted)\n\n\ndef merge_env_files(base_file: str, override_file: str, output_file: str):\n \"\"\"\n Merge two .env files, with override taking precedence.\n\n Args:\n base_file: Base .env file\n override_file: Override .env file\n output_file: Output merged file\n\n Example:\n >>> merge_env_files('.env.base', '.env.local', '.env')\n \"\"\"\n base = load_env_file(base_file)\n override = load_env_file(override_file)\n\n # Merge (override takes precedence)\n merged = {**base, **override}\n\n # Write merged file\n with open(output_file, 'w') as f:\n f.write(f\"# Merged from {base_file} and {override_file}\\n\")\n f.write(f\"# Generated: {datetime.now().isoformat()}\\n\\n\")\n\n for key, value in sorted(merged.items()):\n # Add quotes if value contains spaces\n if ' ' in value:\n value = f'\"{value}\"'\n f.write(f\"{key}={value}\\n\")\n\n print(f\"✓ Merged {base_file} + {override_file} -> {output_file}\")\n\n\ndef print_env_diff(diff: Dict[str, Dict[str, Optional[str]]], file1_name: str, file2_name: str):\n \"\"\"Pretty print environment file differences.\"\"\"\n print(f\"\\n{'='*60}\")\n print(f\"Comparing: {file1_name} vs {file2_name}\")\n print(f\"{'='*60}\\n\")\n\n if diff['only_in_1']:\n print(f\"Only in {file1_name}:\")\n for key, value in diff['only_in_1'].items():\n print(f\" {key}={value[:50]}...\")\n print()\n\n if diff['only_in_2']:\n print(f\"Only in {file2_name}:\")\n for key, value in diff['only_in_2'].items():\n print(f\" {key}={value[:50]}...\")\n print()\n\n if diff['different']:\n print(\"Different values:\")\n for key, values in diff['different'].items():\n print(f\" {key}:\")\n print(f\" {file1_name}: {values['file1'][:50]}...\")\n print(f\" {file2_name}: {values['file2'][:50]}...\")\n print()\n\n if not any(diff.values()):\n print(\"✓ Files are identical\")\n\n\n# CLI Interface\ndef main():\n \"\"\"Command-line interface for env helper utilities.\"\"\"\n parser = argparse.ArgumentParser(\n description='Environment configuration helper utilities',\n formatter_class=argparse.RawDescriptionHelpFormatter,\n epilog=\"\"\"\nExamples:\n # Validate .env file\n python env_helper.py validate .env --required APP_NAME DATABASE_URL\n\n # Check security issues\n python env_helper.py check .env\n\n # Compare environments\n python env_helper.py compare .env.development .env.production\n\n # Generate template\n python env_helper.py template .env .env.template\n\n # Encrypt secrets\n python env_helper.py encrypt secrets.json secrets.encrypted\n\n # Decrypt secrets\n python env_helper.py decrypt secrets.encrypted\n \"\"\"\n )\n\n subparsers = parser.add_subparsers(dest='command', help='Command to run')\n\n # Validate command\n validate_parser = subparsers.add_parser('validate', help='Validate .env file')\n validate_parser.add_argument('file', help='.env file to validate')\n validate_parser.add_argument('--required', nargs='+', help='Required variables')\n\n # Check command\n check_parser = subparsers.add_parser('check', help='Security check .env file')\n check_parser.add_argument('file', help='.env file to check')\n\n # Compare command\n compare_parser = subparsers.add_parser('compare', help='Compare two .env files')\n compare_parser.add_argument('file1', help='First .env file')\n compare_parser.add_argument('file2', help='Second .env file')\n\n # Template command\n template_parser = subparsers.add_parser('template', help='Generate .env template')\n template_parser.add_argument('source', help='Source .env file')\n template_parser.add_argument('output', help='Output template file')\n template_parser.add_argument('--no-mask', action='store_true', help='Keep original values')\n\n # Encrypt command\n encrypt_parser = subparsers.add_parser('encrypt', help='Encrypt secrets file')\n encrypt_parser.add_argument('input', help='Input file (plain text)')\n encrypt_parser.add_argument('output', help='Output file (encrypted)')\n encrypt_parser.add_argument('--password', help='Encryption password (or use stdin)')\n\n # Decrypt command\n decrypt_parser = subparsers.add_parser('decrypt', help='Decrypt secrets file')\n decrypt_parser.add_argument('input', help='Input file (encrypted)')\n decrypt_parser.add_argument('--password', help='Decryption password (or use stdin)')\n decrypt_parser.add_argument('--output', help='Output file (default: stdout)')\n\n # Merge command\n merge_parser = subparsers.add_parser('merge', help='Merge two .env files')\n merge_parser.add_argument('base', help='Base .env file')\n merge_parser.add_argument('override', help='Override .env file')\n merge_parser.add_argument('output', help='Output merged file')\n\n args = parser.parse_args()\n\n if not args.command:\n parser.print_help()\n sys.exit(1)\n\n # Execute command\n if args.command == 'validate':\n valid, missing = validate_env_file(args.file, args.required or [])\n if valid:\n print(f\"✓ {args.file} is valid\")\n else:\n print(f\"❌ {args.file} is missing required variables:\")\n for var in missing:\n print(f\" - {var}\")\n sys.exit(1)\n\n elif args.command == 'check':\n warnings = check_env_security(args.file)\n if warnings:\n print(f\"⚠ Security issues found in {args.file}:\")\n for warning in warnings:\n print(f\" - {warning}\")\n else:\n print(f\"✓ No security issues found in {args.file}\")\n\n elif args.command == 'compare':\n diff = compare_env_files(args.file1, args.file2)\n print_env_diff(diff, args.file1, args.file2)\n\n elif args.command == 'template':\n generate_env_template(args.source, args.output, mask_values=not args.no_mask)\n\n elif args.command == 'encrypt':\n password = args.password\n if not password:\n import getpass\n password = getpass.getpass('Encryption password: ')\n encrypt_secrets(args.input, args.output, password)\n\n elif args.command == 'decrypt':\n password = args.password\n if not password:\n import getpass\n password = getpass.getpass('Decryption password: ')\n secrets = decrypt_secrets(args.input, password)\n\n if args.output:\n with open(args.output, 'w') as f:\n json.dump(secrets, f, indent=2)\n print(f\"✓ Decrypted to {args.output}\")\n else:\n print(json.dumps(secrets, indent=2))\n\n elif args.command == 'merge':\n merge_env_files(args.base, args.override, args.output)\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":16038,"content_sha256":"ad0102ebb45bd20298751fe08605fb7a7e6e4d0b8cdb8b4442352f7d094f5265"},{"filename":"skill-report.json","content":"{\n \"schema_version\": \"2.0\",\n \"meta\": {\n \"generated_at\": \"2026-01-16T18:25:46.446Z\",\n \"slug\": \"autumnsgrove-env-config\",\n \"source_url\": \"https://github.com/AutumnsGrove/ClaudeSkills/tree/master/env-config\",\n \"source_ref\": \"master\",\n \"model\": \"claude\",\n \"analysis_version\": \"3.0.0\",\n \"source_type\": \"community\",\n \"content_hash\": \"83ae2ef0b0bb4b62bdaec2de1f28e1ad5915e82088a541c69633cc8b9dac204a\",\n \"tree_hash\": \"ec955180ada029049c5aa202b44c45e11a9b8202e9b4c6a8b011ec5415c99c96\"\n },\n \"skill\": {\n \"name\": \"env-config\",\n \"description\": \"Environment configuration and secrets management skill using UV for Python projects. Handles .env files, environment variables, secrets encryption, multi-environment setups, and secure configuration patterns. Use when setting up project environments, managing API keys, or implementing configuration best practices.\",\n \"summary\": \"Environment configuration and secrets management skill using UV for Python projects. Handles .env fi...\",\n \"icon\": \"🔐\",\n \"version\": \"1.0.0\",\n \"author\": \"AutumnsGrove\",\n \"license\": \"MIT\",\n \"category\": \"devops\",\n \"tags\": [\n \"environment\",\n \"configuration\",\n \"secrets\",\n \"uv\",\n \"python\"\n ],\n \"supported_tools\": [\n \"claude\",\n \"codex\",\n \"claude-code\"\n ],\n \"risk_factors\": [\n \"scripts\",\n \"env_access\",\n \"filesystem\"\n ]\n },\n \"security_audit\": {\n \"risk_level\": \"low\",\n \"is_blocked\": false,\n \"safe_to_publish\": true,\n \"summary\": \"This is a legitimate environment configuration management skill. The 984 static findings are predominantly false positives triggered by documentation patterns. The skill's core functionality involves reading .env files (expected behavior), using standard cryptography libraries securely, and providing installation examples from official sources. No evidence of malicious intent, data exfiltration, or unauthorized access patterns.\",\n \"risk_factor_evidence\": [\n {\n \"factor\": \"scripts\",\n \"evidence\": [\n {\n \"file\": \"scripts/env_helper.py\",\n \"line_start\": 1,\n \"line_end\": 518\n }\n ]\n },\n {\n \"factor\": \"env_access\",\n \"evidence\": [\n {\n \"file\": \"scripts/env_helper.py\",\n \"line_start\": 25,\n \"line_end\": 70\n }\n ]\n },\n {\n \"factor\": \"filesystem\",\n \"evidence\": [\n {\n \"file\": \"scripts/env_helper.py\",\n \"line_start\": 196,\n \"line_end\": 221\n }\n ]\n }\n ],\n \"critical_findings\": [],\n \"high_findings\": [],\n \"medium_findings\": [],\n \"low_findings\": [],\n \"dangerous_patterns\": [],\n \"files_scanned\": 10,\n \"total_lines\": 3621,\n \"audit_model\": \"claude\",\n \"audited_at\": \"2026-01-16T18:25:46.446Z\"\n },\n \"content\": {\n \"user_title\": \"Configure environment variables and secrets\",\n \"value_statement\": \"Managing environment configurations across multiple environments is error-prone and risky. This skill provides utilities for parsing .env files, validating required variables, encrypting secrets, and comparing configurations between environments.\",\n \"seo_keywords\": [\n \"Claude environment configuration\",\n \"Claude Code secrets management\",\n \"Python dotenv files\",\n \"UV Python package manager\",\n \"environment variables management\",\n \"secrets encryption\",\n \"multi-environment configuration\",\n \"12-factor app config\"\n ],\n \"actual_capabilities\": [\n \"Parse and validate .env files with required variables\",\n \"Check for security issues like placeholder values and short secrets\",\n \"Encrypt and decrypt secrets files using Fernet encryption\",\n \"Generate .env templates from existing configuration files\",\n \"Compare environment files between development and production\"\n ],\n \"limitations\": [\n \"Does not rotate secrets automatically\",\n \"Requires manual password entry for encryption/decryption\",\n \"Does not integrate with external secret managers like Vault\"\n ],\n \"use_cases\": [\n {\n \"target_user\": \"Python developers\",\n \"title\": \"Set up project configuration\",\n \"description\": \"Create secure .env files and templates for new Python projects using UV package manager.\"\n },\n {\n \"target_user\": \"DevOps engineers\",\n \"title\": \"Compare environments\",\n \"description\": \"Validate that development and production environments have matching required configuration.\"\n },\n {\n \"target_user\": \"Security-conscious teams\",\n \"title\": \"Encrypt sensitive config\",\n \"description\": \"Protect secrets.json files with encryption for safer deployment and storage.\"\n }\n ],\n \"prompt_templates\": [\n {\n \"title\": \"Create env template\",\n \"scenario\": \"New Python project setup\",\n \"prompt\": \"Help me create a .env.template file for my Python project with UV. I need database URL, API keys for Anthropic and OpenAI, and JWT secret configuration.\"\n },\n {\n \"title\": \"Validate configuration\",\n \"scenario\": \"Check environment files\",\n \"prompt\": \"Validate my .env.production file has all required variables: DATABASE_URL, SECRET_KEY, and API keys. Report any missing values.\"\n },\n {\n \"title\": \"Encrypt secrets\",\n \"scenario\": \"Secure sensitive config\",\n \"prompt\": \"Create an encrypted version of my secrets.json file. Use a secure password and show how to decrypt it later.\"\n },\n {\n \"title\": \"Compare environments\",\n \"scenario\": \"Configuration audit\",\n \"prompt\": \"Compare my .env.development and .env.production files. List all variables that exist in one but not the other, and any that have different values.\"\n }\n ],\n \"output_examples\": [\n {\n \"input\": \"Create a .env template for my project with database, API keys, and feature flags\",\n \"output\": [\n \"Generated .env.template with sections for Application Settings, Database Configuration, API Keys, Security, and Feature Flags\",\n \"Recommended variables include APP_NAME, APP_ENV, DEBUG, LOG_LEVEL, DATABASE_URL, ANTHROPIC_API_KEY, OPENAI_API_KEY, SECRET_KEY, JWT_SECRET, ENABLE_ANALYTICS, ENABLE_CACHING\",\n \"Added helpful comments for each variable explaining purpose and format\"\n ]\n },\n {\n \"input\": \"Check my .env file for security issues\",\n \"output\": [\n \"Found 3 potential security issues:\",\n \"- API_KEY appears to contain placeholder value: 'your-key-here'\",\n \"- SECRET_KEY seems too short (less than 12 characters)\",\n \"- DATABASE_URL appears to be using placeholder credentials\"\n ]\n },\n {\n \"input\": \"Compare my development and production environments\",\n \"output\": [\n \"Only in .env.development: DEBUG=true, LOG_LEVEL=DEBUG\",\n \"Only in .env.production: SECRET_KEY=[masked], LOG_LEVEL=WARNING\",\n \"Different values: DATABASE_URL (localhost vs prod-host), CACHE_TTL (300 vs 60)\"\n ]\n }\n ],\n \"best_practices\": [\n \"Always add .env and secrets.json files to .gitignore before committing\",\n \"Use different configuration files for each environment (development, staging, production)\",\n \"Generate .env.template files without actual secrets to share with team members\"\n ],\n \"anti_patterns\": [\n \"Never commit actual API keys, passwords, or secrets to version control\",\n \"Avoid using placeholder values like 'password123' or 'changeme' in production\",\n \"Do not use the same secrets across development and production environments\"\n ],\n \"faq\": [\n {\n \"question\": \"What is the difference between .env and .env.template?\",\n \"answer\": \".env contains actual secrets (added to .gitignore). .env.template shows structure with placeholders (safe to commit).\"\n },\n {\n \"question\": \"How does encryption work in env_helper.py?\",\n \"answer\": \"Uses Fernet symmetric encryption with PBKDF2-SHA256 key derivation. Password required for both encryption and decryption.\"\n },\n {\n \"question\": \"Can I use this with Poetry or pip instead of UV?\",\n \"answer\": \"Yes. The skill focuses on UV but concepts apply to any Python project. Replace uv add with pip install for dependencies.\"\n },\n {\n \"question\": \"How do I validate required variables?\",\n \"answer\": \"Use: python scripts/env_helper.py validate .env --required APP_NAME DATABASE_URL SECRET_KEY\"\n },\n {\n \"question\": \"Should I use JSON or .env for secrets?\",\n \"answer\": \".env is simpler for most cases. JSON works better for complex nested structures or when integrating with certain frameworks.\"\n },\n {\n \"question\": \"How do I mask values in generated templates?\",\n \"answer\": \"The generate_env_template function automatically masks values with 'KEY', 'SECRET', 'PASSWORD', or 'TOKEN' in the variable name.\"\n }\n ]\n },\n \"file_structure\": [\n {\n \"name\": \"examples\",\n \"type\": \"dir\",\n \"path\": \"examples\",\n \"children\": [\n {\n \"name\": \"pyproject.toml\",\n \"type\": \"file\",\n \"path\": \"examples/pyproject.toml\",\n \"lines\": 190\n },\n {\n \"name\": \"secrets_template.json\",\n \"type\": \"file\",\n \"path\": \"examples/secrets_template.json\",\n \"lines\": 105\n }\n ]\n },\n {\n \"name\": \"references\",\n \"type\": \"dir\",\n \"path\": \"references\",\n \"children\": [\n {\n \"name\": \"advanced-topics.md\",\n \"type\": \"file\",\n \"path\": \"references/advanced-topics.md\",\n \"lines\": 400\n },\n {\n \"name\": \"api-reference.md\",\n \"type\": \"file\",\n \"path\": \"references/api-reference.md\",\n \"lines\": 307\n },\n {\n \"name\": \"testing-guide.md\",\n \"type\": \"file\",\n \"path\": \"references/testing-guide.md\",\n \"lines\": 452\n },\n {\n \"name\": \"troubleshooting.md\",\n \"type\": \"file\",\n \"path\": \"references/troubleshooting.md\",\n \"lines\": 576\n }\n ]\n },\n {\n \"name\": \"scripts\",\n \"type\": \"dir\",\n \"path\": \"scripts\",\n \"children\": [\n {\n \"name\": \"env_helper.py\",\n \"type\": \"file\",\n \"path\": \"scripts/env_helper.py\",\n \"lines\": 518\n }\n ]\n },\n {\n \"name\": \"README.md\",\n \"type\": \"file\",\n \"path\": \"README.md\",\n \"lines\": 235\n },\n {\n \"name\": \"SKILL.md\",\n \"type\": \"file\",\n \"path\": \"SKILL.md\",\n \"lines\": 527\n }\n ]\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":10811,"content_sha256":"8a6918a6fc3144a228fcc9e422414b8cca5d5a9a497cd03ba9c61077bbbb75de"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Environment Configuration Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill provides comprehensive guidance for managing environment configurations, secrets, and environment variables in Python projects using UV (the modern Python package and project manager). It covers secure configuration patterns, multi-environment setups, .env file management, and secrets handling with encryption support.","type":"text"}]},{"type":"paragraph","content":[{"text":"Environment configuration is critical for separating configuration from code (12-factor app principles), managing secrets securely across environments, preventing credential leaks, and supporting team collaboration.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when you need to:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set up environment configuration for a new Python project","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Implement secure secrets management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configure multi-environment setups (dev/staging/prod)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Migrate from hardcoded configs to environment variables","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Audit existing configuration for security issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Standardize configuration across team projects","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Set up UV-based Python project with proper config management","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Principles","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Never Hardcode Secrets","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All API keys, passwords, tokens go in environment variables or encrypted secrets","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configuration files with secrets must be in .gitignore","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use templates for sharing structure, not actual secrets","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Separate by Environment","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Different configurations for development, staging, production","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Environment-specific .env files (.env.development, .env.production)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clear naming conventions for environment variables","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Fail Securely","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate required environment variables on startup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Provide clear error messages for missing configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use sensible defaults only for non-sensitive values","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Use UV for Dependency Management","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"UV provides fast, reliable Python package management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Replaces pip, pip-tools, virtualenv, and more","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensures reproducible environments across machines","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. Document Everything","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Template files show structure without exposing secrets","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"README explains required variables and how to set them","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Comments describe purpose and format of variables","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"UV Setup","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Installing UV","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Or with pip\npip install uv\n\n# Verify installation\nuv --version","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Initialize UV Project","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Create new project\nuv init my-project\ncd my-project\n\n# Create virtual environment\nuv venv\nsource .venv/bin/activate # On Windows: .venv\\Scripts\\activate\n\n# Add dependencies\nuv add python-dotenv cryptography pydantic\n\n# Add dev dependencies\nuv add --dev pytest pytest-env black ruff","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Environment Configuration Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: Project Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"1. Create Project Structure","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Initialize UV project\nuv init your-project-name\ncd your-project-name\nuv venv\nsource .venv/bin/activate","type":"text"}]},{"type":"paragraph","content":[{"text":"2. Install Configuration Dependencies","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"uv add python-dotenv # For .env file loading\nuv add cryptography # For secrets encryption (optional)\nuv add pydantic # For config validation (optional)\nuv add --dev pytest pytest-env","type":"text"}]},{"type":"paragraph","content":[{"text":"3. Create Configuration Files","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Essential files to create:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env.template","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Template showing required variables (commit this)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Actual secrets (add to .gitignore)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env.development","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Development-specific config","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env.production","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Production-specific config","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"config.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Configuration loading module","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"4. Update .gitignore","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Add to .gitignore\ncat >> .gitignore \u003c\u003c 'EOF'\n# Environment files\n.env\n.env.local\n.env.*.local\nsecrets.json\n\n# UV\n.venv/\n__pycache__/\n*.pyc\n.pytest_cache/\n.ruff_cache/\nEOF","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: Create Configuration Templates","type":"text"}]},{"type":"paragraph","content":[{"text":"1. Create .env.template","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# .env.template - Commit this file\n# Copy to .env and fill in actual values\n\n# Application Settings\nAPP_NAME=MyApp\nAPP_ENV=development\nDEBUG=true\nLOG_LEVEL=INFO\n\n# Database Configuration\nDATABASE_URL=postgresql://user:password@localhost:5432/dbname\nDATABASE_POOL_SIZE=5\n\n# API Keys (Replace with actual keys)\nANTHROPIC_API_KEY=sk-ant-api03-xxx\nOPENAI_API_KEY=sk-xxx\nOPENROUTER_API_KEY=sk-or-v1-xxx\n\n# Security\nSECRET_KEY=generate-random-secret-key-here\nJWT_SECRET=another-random-secret\n\n# Feature Flags\nENABLE_ANALYTICS=false\nENABLE_CACHING=true","type":"text"}]},{"type":"paragraph","content":[{"text":"2. Create Config Loading Module","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Create ","type":"text"},{"text":"config.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - see ","type":"text"},{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete implementation:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import os\nfrom pathlib import Path\nfrom dotenv import load_dotenv\n\n\nclass ConfigError(Exception):\n \"\"\"Raised when required configuration is missing.\"\"\"\n pass\n\n\nclass Config:\n \"\"\"Application configuration from environment variables.\"\"\"\n\n def __init__(self, env: str = None):\n self.env = env or os.getenv('APP_ENV', 'development')\n self._load_env_file()\n self._validate_required()\n\n def _load_env_file(self):\n \"\"\"Load appropriate .env file based on environment.\"\"\"\n env_file = Path(f'.env.{self.env}')\n if env_file.exists():\n load_dotenv(env_file, override=True)\n if Path('.env').exists():\n load_dotenv('.env', override=False)\n\n @property\n def app_name(self) -> str:\n return os.getenv('APP_NAME', 'MyApp')\n\n @property\n def debug(self) -> bool:\n return os.getenv('DEBUG', 'false').lower() in ('true', '1', 'yes')\n\n # Add more properties as needed...\n\n\n# Global config instance\nconfig = Config()","type":"text"}]},{"type":"paragraph","content":[{"text":"For complete Config class with all properties and validation, see ","type":"text"},{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"3. Use Config in Application","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from config import config\n\ndef main():\n print(f\"Starting {config.app_name} in {config.app_env} mode\")\n\n if config.anthropic_api_key:\n # Use API key\n print(\"✓ API key loaded\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Multi-Environment Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"1. Create Environment-Specific Files","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":".env.development","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"APP_ENV=development\nDEBUG=true\nLOG_LEVEL=DEBUG\nDATABASE_URL=postgresql://localhost:5432/myapp_dev","type":"text"}]},{"type":"paragraph","content":[{"text":".env.production","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"APP_ENV=production\nDEBUG=false\nLOG_LEVEL=WARNING\nDATABASE_URL=postgresql://prod-host:5432/myapp_prod\nSECRET_KEY=super-secure-random-key","type":"text"}]},{"type":"paragraph","content":[{"text":"2. Switch Between Environments","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Development (default)\nuv run python main.py\n\n# Production\nexport APP_ENV=production\nuv run python main.py","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Secrets Management","type":"text"}]},{"type":"paragraph","content":[{"text":"1. Using JSON Secrets (Alternative Pattern)","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"Create ","type":"text"},{"text":"secrets_template.json","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"anthropic_api_key\": \"sk-ant-api03-xxx\",\n \"openai_api_key\": \"sk-xxx\",\n \"database_password\": \"your-password-here\",\n \"comment\": \"Copy to secrets.json and fill in real values\"\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"2. Load JSON Secrets","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete implementation:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import json\nfrom pathlib import Path\n\ndef load_secrets(secrets_file: str = 'secrets.json') -> dict:\n \"\"\"Load secrets from JSON with fallback to env vars.\"\"\"\n if Path(secrets_file).exists():\n return json.load(open(secrets_file))\n # Fallback to environment variables\n return {\n 'anthropic_api_key': os.getenv('ANTHROPIC_API_KEY', '')\n }","type":"text"}]},{"type":"paragraph","content":[{"text":"3. Encrypted Secrets","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"For encryption utilities and advanced secrets management, see:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/advanced-topics.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Encryption, rotation, auditing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/env_helper.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Encryption/decryption utilities","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Configuration Validation","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Using Pydantic for Type-Safe Config","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import BaseSettings, Field, validator\n\n\nclass Settings(BaseSettings):\n app_name: str = Field(default='MyApp', env='APP_NAME')\n app_env: str = Field(default='development', env='APP_ENV')\n database_url: str = Field(..., env='DATABASE_URL') # Required\n\n @validator('app_env')\n def validate_env(cls, v):\n allowed = ['development', 'staging', 'production']\n if v not in allowed:\n raise ValueError(f'app_env must be one of {allowed}')\n return v\n\n class Config:\n env_file = '.env'\n\n\nsettings = Settings()","type":"text"}]},{"type":"paragraph","content":[{"text":"For complete Pydantic configuration examples, see ","type":"text"},{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Best Practices","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. .gitignore Configuration","type":"text"}]},{"type":"paragraph","content":[{"text":"Always add to ","type":"text"},{"text":".gitignore","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":".env\n.env.local\n.env.*.local\nsecrets.json\n.env.production\n.venv/","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Secret Rotation","type":"text"}]},{"type":"paragraph","content":[{"text":"Implement regular API key rotation:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"def rotate_api_key(old_key: str, new_key: str):\n \"\"\"Rotate API key gracefully with backup.\"\"\"\n # Implementation in references/advanced-topics.md","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Environment Auditing","type":"text"}]},{"type":"paragraph","content":[{"text":"Regular security audits:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"def audit_environment():\n \"\"\"Check for security issues in environment variables.\"\"\"\n # Implementation in references/advanced-topics.md","type":"text"}]},{"type":"paragraph","content":[{"text":"For complete security implementations, see ","type":"text"},{"text":"references/advanced-topics.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Testing Configuration","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Test Setup","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# conftest.py\nimport pytest\nimport os\n\n\[email protected]\ndef test_env():\n \"\"\"Set up test environment variables.\"\"\"\n original = os.environ.copy()\n os.environ['APP_ENV'] = 'testing'\n os.environ['DEBUG'] = 'true'\n yield\n os.environ.clear()\n os.environ.update(original)\n\n\[email protected]\ndef config(test_env):\n from config import Config\n return Config(env='testing')","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example Tests","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"def test_config_loading(config):\n assert config.app_env == 'testing'\n assert config.debug is True\n\n\ndef test_missing_required_var():\n from config import ConfigError\n with pytest.raises(ConfigError):\n Config() # Missing required vars","type":"text"}]},{"type":"paragraph","content":[{"text":"For comprehensive testing guide, see ","type":"text"},{"text":"references/testing-guide.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"UV Commands Reference","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Dependency management\nuv sync # Install all dependencies\nuv add requests # Add dependency\nuv add --dev pytest # Add dev dependency\nuv remove requests # Remove dependency\n\n# Environment management\nuv venv # Create virtual environment\nuv lock --upgrade # Update dependencies\n\n# Running scripts\nuv run python main.py # Run with UV environment\nuv run pytest # Run tests\n\n# Compatibility\nuv pip compile pyproject.toml -o requirements.txt","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Helper Scripts","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill provides utility scripts in ","type":"text"},{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"env_helper.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Core utilities for env management","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Parse and validate .env files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check for missing variables","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Encrypt/decrypt secrets files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Compare environments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate .env templates","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Usage:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Validate .env file\npython scripts/env_helper.py validate .env\n\n# Encrypt secrets\npython scripts/env_helper.py encrypt secrets.json secrets.encrypted\n\n# Compare environments\npython scripts/env_helper.py compare .env.development .env.production","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Setup Checklist","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Install UV: ","type":"text"},{"text":"curl -LsSf https://astral.sh/uv/install.sh | sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Create project: ","type":"text"},{"text":"uv init project-name","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Create virtual env: ","type":"text"},{"text":"uv venv","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Add dependencies: ","type":"text"},{"text":"uv add python-dotenv","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Create ","type":"text"},{"text":".env.template","type":"text","marks":[{"type":"code_inline"}]},{"text":" from examples","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Copy to ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" and fill in secrets","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":".gitignore","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Create ","type":"text"},{"text":"config.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" module","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Test configuration loading","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Set up environment-specific configs","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Implement validation","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Add tests for configuration","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Environment Variable Naming","type":"text"}]},{"type":"paragraph","content":[{"text":"Use consistent naming:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"APP_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Application settings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DATABASE_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Database configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"*_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":" - API keys and tokens","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"*_SECRET","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Secret keys","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ENABLE_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Feature flags","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"*_URL","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Service endpoints","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Security Checklist","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No secrets in version control","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":".gitignore","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Production secrets separate from dev","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Required variables validated on startup","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Secrets encrypted at rest (if needed)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Regular secret rotation","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Team training on secure practices","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Additional Resources","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Documentation in This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" - Complete Config class implementation, Pydantic validation, JSON secrets loading","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/advanced-topics.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" - Encryption, secret rotation, auditing, multi-tenant config, cloud integration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/testing-guide.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" - pytest setup, mocking, validation testing, CI/CD examples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/troubleshooting.md","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" - Common issues, environment-specific problems, debugging techniques","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Examples Directory","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Comprehensive .env template","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":" - UV project configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"secrets_template.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" - JSON secrets template","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"External Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"UV Documentation","type":"text","marks":[{"type":"strong"}]},{"text":": https://docs.astral.sh/uv/","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"python-dotenv","type":"text","marks":[{"type":"strong"}]},{"text":": https://github.com/theskumar/python-dotenv","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pydantic","type":"text","marks":[{"type":"strong"}]},{"text":": https://docs.pydantic.dev/","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"12-Factor App Config","type":"text","marks":[{"type":"strong"}]},{"text":": https://12factor.net/config","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"paragraph","content":[{"text":"For common issues and solutions, see ","type":"text"},{"text":"references/troubleshooting.md","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Environment variables not loading","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Wrong environment loaded","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"UV sync failures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Import errors","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Secrets decryption issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Performance optimization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security issues and remediation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docker integration problems","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Quick debug:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from dotenv import load_dotenv\nload_dotenv(verbose=True) # Shows loading process","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"env-config","author":"@skillopedia","source":{"stars":336,"repo_name":"marketplace","origin_url":"https://github.com/aiskillstore/marketplace/blob/HEAD/skills/autumnsgrove/env-config/SKILL.md","repo_owner":"aiskillstore","body_sha256":"55d33a8fc9658a2d5a2bf9399266879326e3f212f44858f20d5896ed3f21b5b6","cluster_key":"0989ba15c69cd125c25d54ba76f54b97579d880255095e5a1e0c07b9ad1bfd0b","clean_bundle":{"format":"clean-skill-bundle-v1","source":"aiskillstore/marketplace/skills/autumnsgrove/env-config/SKILL.md","attachments":[{"id":"809b4dec-92f3-5d88-96f4-4be9103c890a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/809b4dec-92f3-5d88-96f4-4be9103c890a/attachment.md","path":"README.md","size":5196,"sha256":"93106ee4006b23e67f8dfdb380cdf6ed20d34f276d7ee67a28b4c91cbd7caea7","contentType":"text/markdown; charset=utf-8"},{"id":"879fdb77-32f9-5250-9a35-c21a813be221","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/879fdb77-32f9-5250-9a35-c21a813be221/attachment.toml","path":"examples/pyproject.toml","size":4158,"sha256":"472e2f711d7176ab59883c13338596a1d940af78968aac385fb9e8ac7f363f24","contentType":"text/plain; charset=utf-8"},{"id":"c31638cc-e38c-5ecd-9baa-4d4859a91ee4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c31638cc-e38c-5ecd-9baa-4d4859a91ee4/attachment.json","path":"examples/secrets_template.json","size":2591,"sha256":"5b0216323e4aa8b8797769b05d888f433ca37fcbd9c5eac51b8ca252f6c12129","contentType":"application/json; charset=utf-8"},{"id":"d7914bf2-1abd-5b41-bb13-a9f64337142f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d7914bf2-1abd-5b41-bb13-a9f64337142f/attachment.md","path":"references/advanced-topics.md","size":10428,"sha256":"0dbd4604ade0cfe4af4582397edd2aa9201c3701ce524e3abf8c69f7e8b4ced2","contentType":"text/markdown; charset=utf-8"},{"id":"14fa23cb-8dd2-5557-966b-85f540dbb572","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/14fa23cb-8dd2-5557-966b-85f540dbb572/attachment.md","path":"references/api-reference.md","size":8129,"sha256":"c0b6470c32568439d13628a30e4faf1b45279cabac45c765d9fa0d2878946d78","contentType":"text/markdown; charset=utf-8"},{"id":"6f4e3c1e-7431-5124-858b-aac6d956266c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6f4e3c1e-7431-5124-858b-aac6d956266c/attachment.md","path":"references/testing-guide.md","size":10426,"sha256":"69744ec15b5bccf23cb6d04eee7e86a39bc34d50cf4363be966cea9efc02253f","contentType":"text/markdown; charset=utf-8"},{"id":"537c8aca-5c60-5ac2-a3e2-0f9e6ff5133b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/537c8aca-5c60-5ac2-a3e2-0f9e6ff5133b/attachment.md","path":"references/troubleshooting.md","size":11396,"sha256":"44b8d36b48604a330c5e567d91afd29740199e2d94719c4a83672bf99b124297","contentType":"text/markdown; charset=utf-8"},{"id":"d22458e4-b5ce-5386-bbd2-f2c635e1a9e9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d22458e4-b5ce-5386-bbd2-f2c635e1a9e9/attachment.py","path":"scripts/env_helper.py","size":16038,"sha256":"ad0102ebb45bd20298751fe08605fb7a7e6e4d0b8cdb8b4442352f7d094f5265","contentType":"text/x-python; charset=utf-8"},{"id":"4c3bfbfe-8317-5690-b83c-4bbbc848b44e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4c3bfbfe-8317-5690-b83c-4bbbc848b44e/attachment.json","path":"skill-report.json","size":10811,"sha256":"8a6918a6fc3144a228fcc9e422414b8cca5d5a9a497cd03ba9c61077bbbb75de","contentType":"application/json; charset=utf-8"}],"bundle_sha256":"0efcbfab06c96905feabaacd261f7628da4aeaa9b6a0e207b57613604ee53c0b","attachment_count":9,"text_attachments":9,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/autumnsgrove/env-config/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"integrations-apis","import_tag":"clean-skills-v1","description":"Environment configuration and secrets management skill using UV for Python projects. Handles .env files, environment variables, secrets encryption, multi-environment setups, and secure configuration patterns. Use when setting up project environments, managing API keys, or implementing configuration best practices."}},"renderedAt":1782980202583}

Environment Configuration Skill Overview This skill provides comprehensive guidance for managing environment configurations, secrets, and environment variables in Python projects using UV (the modern Python package and project manager). It covers secure configuration patterns, multi-environment setups, .env file management, and secrets handling with encryption support. Environment configuration is critical for separating configuration from code (12-factor app principles), managing secrets securely across environments, preventing credential leaks, and supporting team collaboration. When to Use…