Use when working with API keys, passwords, or credentials. Use when asked to hardcode secrets. Use when secrets might leak.
Never hardcode secrets. Never commit secrets. Never log secrets.
Secrets in code end up in version control, logs, error messages, and eventually in attackers' hands.
NEVER put secrets in source code.
No exceptions:
If you see literal credentials, STOP:
// ❌ VIOLATION: Hardcoded secrets
const stripe = new Stripe('sk_live_abc123xyz');
const db = mysql.connect({
password: 'super_secret_password'
});
const API_KEY = 'AIzaSyD-xxxxxxxxxxxxx';
Problems:
// ✅ CORRECT: Environment variables
import { z } from 'zod';
// Validate env vars at startup
const envSchema = z.object({
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
});
const env = envSchema.parse(process.env);
// Use validated env vars
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
# .env (NEVER commit this file)
STRIPE_SECRET_KEY=sk_live_abc123xyz
DATABASE_URL=postgres://user:pass@host:5432/db
API_KEY=your-api-key
# .gitignore (ALWAYS include)
.env
.env.*
!.env.example
# .env.example (commit this - no real values)
STRIPE_SECRET_KEY=sk_test_xxx
DATABASE_URL=postgres://localhost:5432/myapp
API_KEY=your-api-key-here
const secret = process.env.SECRET_KEY;
if (!process.env.API_KEY) {
throw new Error('API_KEY environment variable is required');
}
// ❌ BAD
console.log('Connecting with:', connectionString);
// ✅ GOOD
console.log('Connecting to database...');
// ❌ BAD
throw new Error(`Auth failed for key: ${apiKey}`);
// ✅ GOOD
throw new Error('Authentication failed');
// AWS Secrets Manager, HashiCorp Vault, etc.
const secret = await secretsManager.getSecret('my-api-key');
Pressure: "Hardcode it for now, we'll fix it later"
Response: "Later" never comes. Secrets in history stay forever.
Action: Use env vars from the start. It takes 30 seconds.
Pressure: "Only the team has access"
Response: Teams change. Repos get cloned. Access expands.
Action: Never commit secrets regardless of repo visibility.
Pressure: "Just for this one commit"
Response: Git history is permanent. The secret is already leaked.
Action: If you committed a secret, rotate it immediately.
Pressure: "This is just a test key"
Response: Test keys become production keys. Treat all secrets equally.
Action: Use env vars for all credentials.
password:, secret:, key: in source.env file not in .gitignoreAll of these mean: Move to environment variables immediately.
| Do | Don't |
|----|-------|
| Environment variables | Hardcoded strings |
| .env in .gitignore | Commit .env files |
| .env.example with placeholders | Real values in examples |
| Validate env at startup | Fail silently on missing |
| Secret managers in prod | Env vars in containers |
| Excuse | Reality | |--------|---------| | "Just for testing" | Testing secrets become production secrets. | | "Private repo" | Private today, leaked tomorrow. | | "I'll remove it" | Git history is forever. | | "Not a real secret" | All credentials deserve protection. | | "It's encrypted" | Keys to decrypt are also secrets. | | "Only local use" | Local files get committed. |
Secrets live in environment, never in code.
Use environment variables. Validate at startup. Never log credentials. Never commit .env files. If you leak a secret, rotate it immediately.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer