Skill v1.0.0
currentTrusted Publisher100/100version: "1.0.0" name: secret-handling description: Never read .env files or write secrets to .squad/ committed files domain: security, file-operations, team-collaboration confidence: high source: earned (issue #267 — credential leak incident)
Context
Spawned agents have read access to the entire repository, including .env files containing live credentials. If an agent reads secrets and writes them to .squad/ files (decisions, logs, history), Scribe auto-commits them to git, exposing them in remote history. This skill codifies absolute prohibitions and safe alternatives.
Patterns
Prohibited File Reads
NEVER read these files:
.env(production secrets).env.local(local dev secrets).env.production(production environment).env.development(development environment).env.staging(staging environment).env.test(test environment with real credentials)- Any file matching
.env.*UNLESS explicitly allowed (see below)
Allowed alternatives:
.env.example(safe — contains placeholder values, no real secrets).env.sample(safe — documentation template).env.template(safe — schema/structure reference)
If you need config info:
- Ask the user directly — "What's the database connection string?"
- Read `.env.example` — shows structure without exposing secrets
- Read documentation — check
README.md,docs/, config guides
NEVER assume you can "just peek at .env to understand the schema." Use .env.example or ask.
Prohibited Output Patterns
NEVER write these to `.squad/` files:
| Pattern Type | Examples | Regex Pattern (for scanning) | ||||||
|---|---|---|---|---|---|---|---|---|
| API Keys | OPENAI_API_KEY=sk-proj-..., GITHUB_TOKEN=ghp_... | `[A-Z_]+(?:KEY | TOKEN | SECRET)=[^\s]+` | ||||
| Passwords | DB_PASSWORD=super_secret_123, password: "..." | `(?:PASSWORD | PASS | PWD)[:=]\s*["']?[^\s"']+` | ||||
| Connection Strings | postgres://user:pass@host:5432/db, Server=...;Password=... | `(?:postgres | mysql | mongodb)://[^@]+@ | (?:Server | Host)=.*(?:Password | Pwd)=` | |
| JWT Tokens | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... | eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+ | ||||||
| Private Keys | -----BEGIN PRIVATE KEY-----, -----BEGIN RSA PRIVATE KEY----- | -----BEGIN [A-Z ]+PRIVATE KEY----- | ||||||
| AWS Credentials | AKIA..., aws_secret_access_key=... | `AKIA[0-9A-Z]{16} | aws_secret_access_key=[^\s]+` | |||||
| Email Addresses | user@example.com (PII violation per team decision) | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} |
What to write instead:
- Placeholder values:
DATABASE_URL=<set in .env> - Redacted references:
API key configured (see .env.example) - Architecture notes: "App uses JWT auth — token stored in session"
- Schema documentation: "Requires OPENAI_API_KEY, GITHUB_TOKEN (see .env.example for format)"
Scribe Pre-Commit Validation
Before committing `.squad/` changes, Scribe MUST:
- Scan all staged files for secret patterns (use regex table above)
- Check for prohibited file names (don't commit
.enveven if manually staged) - If secrets detected:
- STOP the commit (do NOT proceed)
- Remove the file from staging:
git reset HEAD <file> - Report to user:
``` 🚨 SECRET DETECTED — commit blocked
File: .squad/decisions/inbox/river-db-config.md Pattern: DATABASE_URL=postgres://user:password@localhost:5432/prod
This file contains credentials and MUST NOT be committed. Please remove the secret, replace with placeholder, and try again. ```
- Exit with error (never silently skip)
- If no secrets detected:
- Proceed with commit as normal
Implementation note for Scribe:
- Run validation AFTER staging files, BEFORE calling
git commit - Use PowerShell
Select-Stringorgit diff --cachedto scan staged content - Fail loud — secret leaks are unacceptable, blocking the commit is correct behavior
Remediation — If a Secret Was Already Committed
If you discover a secret in git history:
- STOP immediately — do not make more commits
- Alert the user:
``` 🚨 CREDENTIAL LEAK DETECTED
A secret was found in git history: Commit: abc1234 File: .squad/decisions/inbox/agent-config.md Pattern: API_KEY=sk-proj-...
This requires immediate remediation:
- Revoke the exposed credential (regenerate API key, rotate password)
- Remove from git history (git filter-repo or BFG)
- Force-push the cleaned history
Do NOT proceed with new work until this is resolved. ```
- Do NOT attempt to fix it yourself — secret removal requires specialized tools
- Wait for user confirmation before resuming work
Examples
✓ Correct: Reading Config Schema
Agent needs to know what environment variables are required:
Agent: "What environment variables does this app need?"→ Reads `.env.example`:OPENAI_API_KEY=sk-...DATABASE_URL=postgres://user:pass@localhost:5432/dbREDIS_URL=redis://localhost:6379→ Writes to .squad/decisions/inbox/river-env-setup.md:"App requires three environment variables:- OPENAI_API_KEY (OpenAI API key, format: sk-...)- DATABASE_URL (Postgres connection string)- REDIS_URL (Redis connection string)See .env.example for full schema."
✗ Incorrect: Reading Live Credentials
Agent needs to know database schema:
Agent: (reads .env)DATABASE_URL=postgres://admin:super_secret_pw@prod.example.com:5432/appdb→ Writes to .squad/decisions/inbox/river-db-schema.md:"Database connection: postgres://admin:super_secret_pw@prod.example.com:5432/appdb"🚨 VIOLATION: Live credential written to committed file
Correct approach:
Agent: (reads .env.example OR asks user)User: "It's a Postgres database, schema is in migrations/"→ Writes to .squad/decisions/inbox/river-db-schema.md:"Database: Postgres (connection configured in .env). Schema defined in db/migrations/."
✓ Correct: Scribe Pre-Commit Validation
Scribe is about to commit:
# Stage filesgit add .squad/# Scan staged content for secrets$stagedContent = git diff --cached$secretPatterns = @('[A-Z_]+(?:KEY|TOKEN|SECRET)=[^\s]+','(?:PASSWORD|PASS|PWD)[:=]\s*["'']?[^\s"'']+','eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+')$detected = $falseforeach ($pattern in $secretPatterns) {if ($stagedContent -match $pattern) {$detected = $trueWrite-Host "🚨 SECRET DETECTED: $($matches[0])"break}}if ($detected) {# Remove from staging, report, exitgit reset HEAD .squad/Write-Error "Commit blocked — secret detected in staged files"exit 1}# Safe to commitgit commit -F $msgFile
Anti-Patterns
- ❌ Reading
.env"just to check the schema" — use.env.exampleinstead - ❌ Writing "sanitized" connection strings that still contain credentials
- ❌ Assuming "it's just a dev environment" makes secrets safe to commit
- ❌ Committing first, scanning later — validation MUST happen before commit
- ❌ Silently skipping secret detection — fail loud, never silent
- ❌ Trusting agents to "know better" — enforce at multiple layers (prompt, hook, architecture)
- ❌ Writing secrets to "temporary" files in
.squad/— Scribe commits ALL.squad/changes - ❌ Extracting "just the host" from a connection string — still leaks infrastructure topology