From claude-initial-setup
Manage secrets, API keys, and sensitive configuration securely. Activate whenever the user handles API keys, passwords, tokens, database credentials, .env files, CI/CD pipelines, or any configuration that contains sensitive values. Also activate when the user asks about secret rotation, vault integration, or gitignore setup.
npx claudepluginhub versoxbt/claude-initial-setup --plugin claude-initial-setupThis skill uses the workspace's default tool permissions.
Secure handling of API keys, passwords, tokens, and sensitive configuration across
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Secure handling of API keys, passwords, tokens, and sensitive configuration across development, CI/CD, and production environments. Never hardcode secrets; always use environment variables, secret managers, or vault solutions.
Use .env files for local development, never commit them to version control.
# .env (NEVER committed to git)
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
JWT_SECRET=your-256-bit-secret-here
REDIS_URL=redis://localhost:6379
# .env.example (committed to git, no real values)
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
OPENAI_API_KEY=sk-proj-your-key-here
JWT_SECRET=generate-a-secure-random-string
REDIS_URL=redis://localhost:6379
# .gitignore - ALWAYS include these
.env
.env.local
.env.*.local
*.pem
*.key
credentials.json
service-account.json
Always validate that required secrets exist at startup, not at first use.
import { z } from 'zod';
import dotenv from 'dotenv';
dotenv.config();
const envSchema = z.object({
DATABASE_URL: z.string().url(),
OPENAI_API_KEY: z.string().startsWith('sk-'),
JWT_SECRET: z.string().min(32),
NODE_ENV: z.enum(['development', 'staging', 'production']),
PORT: z.coerce.number().default(3000),
});
function loadConfig() {
const result = envSchema.safeParse(process.env);
if (!result.success) {
const missing = result.error.issues.map(i => i.path.join('.'));
throw new Error(`Missing or invalid env vars: ${missing.join(', ')}`);
}
return result.data;
}
export const config = loadConfig();
# Python equivalent with Pydantic
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
openai_api_key: str
jwt_secret: str
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
Use Vault for production secret management with automatic rotation.
import Vault from 'node-vault';
const vault = Vault({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN,
});
async function getSecret(path: string): Promise<string> {
try {
const result = await vault.read(`secret/data/${path}`);
return result.data.data.value;
} catch (error) {
throw new Error(`Failed to read secret at ${path}: ${error.message}`);
}
}
// Usage
const dbPassword = await getSecret('production/database');
Configure secrets in CI/CD without exposing them in logs or artifacts.
# GitHub Actions - use repository secrets
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
run: |
# Never echo secrets
npm run deploy
# WRONG: This exposes secrets in logs
# - run: echo ${{ secrets.API_KEY }}
# WRONG: This exposes secrets in artifacts
# - run: env > debug.txt
# GitLab CI - use protected variables
deploy:
stage: deploy
variables:
DATABASE_URL: $DATABASE_URL # Set in GitLab CI/CD settings
script:
- npm run deploy
only:
- main
Implement graceful secret rotation without downtime.
interface SecretProvider {
getCurrent(): Promise<string>;
getPrevious(): Promise<string | null>;
}
// Accept both current and previous secrets during rotation window
async function validateApiKey(
key: string,
provider: SecretProvider
): Promise<boolean> {
const current = await provider.getCurrent();
if (key === current) return true;
const previous = await provider.getPrevious();
if (previous && key === previous) return true;
return false;
}
// Rotate JWT secrets gracefully
function verifyToken(token: string): JwtPayload {
try {
return jwt.verify(token, config.JWT_SECRET) as JwtPayload;
} catch {
if (config.JWT_SECRET_PREVIOUS) {
return jwt.verify(token, config.JWT_SECRET_PREVIOUS) as JwtPayload;
}
throw new Error('Invalid token');
}
}
const apiKey = "sk-...")| Context | Solution |
|---|---|
| Local development | .env files + dotenv, validated at startup |
| CI/CD pipelines | Platform secret storage (GitHub Secrets, GitLab Variables) |
| Production | Vault, AWS Secrets Manager, GCP Secret Manager |
| Rotation | Dual-key acceptance window, then revoke old key |
| Frontend | Never store secrets; proxy through backend API |
| Git protection | .gitignore, pre-commit hooks, git-secrets scanner |
| Validation | Zod/Pydantic schema at app startup |