This repository has multiple layers of protection against accidentally committing secrets.
- Discord tokens
- API keys and secrets
- Database credentials
- AWS keys
- Private keys
.env- Environment file with secrets.env.local- Local overrides.env.*.local- Environment-specific secrets- See
.gitignorefor the full list
Use .env.example template instead of actual .env:
# WRONG ❌
echo "DISCORD_TOKEN=your_actual_token" > .env
git add .env
# RIGHT ✅
cp .env.example .env
# Edit .env with your real token (stays local)
# .env is already in .gitignoreUpdate .env.example with placeholder values:
# .env.example
DISCORD_TOKEN=your_discord_bot_token_here
DISCORD_GUILD_ID=your_guild_id_here
DISCORD_CHANNEL_ID=your_channel_id_here
DISCORD_CODE_AUTHOR_ID=your_code_author_bot_id_here
# Required: generate with: openssl rand -hex 32
ENCRYPTION_KEY=your_64_char_hex_key_here
DB_PATH=/app/data/idle.db
NODE_ENV=developmentFor team members:
- Use GitHub Secrets (for CI/CD)
- Use encrypted password manager
- Share
.envfile via secure channel (Signal, 1Password, etc.) - Never email or Slack secrets
When you run git commit, the pre-commit hooks (in .husky/) run automatically to protect your code:
- ✅ Gitleaks - Scans for hardcoded secrets (API keys, tokens, credentials)
- ✅ ESLint - TypeScript/formatting checks via lint-staged
- ✅ Commitlint (commit-msg hook) - Validates commit message format
The hooks use the local bin/mise to ensure consistent tool versions:
# In .husky/pre-commit:
"$PROJECT_ROOT/bin/mise" run gitleaks detect --source . --verbose
"$PROJECT_ROOT/bin/mise" run -- bunx lint-staged
# In .husky/commit-msg:
"$PROJECT_ROOT/bin/mise" run -- bunx commitlint --edit "$1"If any check fails, the commit is rejected. Fix and retry:
# See what failed
git commit -m "..."
# Fix Gitleaks warnings
# - Remove secrets from files
# - Update .gitignore if needed
# Fix ESLint issues
mise run lint:fix
# Stage fixes and try again
git add .
git commit -m "..."--no-verify in normal workflow
If you absolutely must bypass (not recommended):
git commit --no-verifyThis bypasses ALL hook checks including secret scanning. Only use in emergencies.
Additional scanning runs on every push and PR:
- Scans entire repository history
- Checks for API keys, tokens, credentials
- Runs on every push and PR
- Detects high-entropy strings (likely secrets)
- Searches for unused credentials
- Runs on every push and PR
- Ensures
.envfiles are NOT tracked - Warns if sensitive keywords appear in code
- Validates proper secret management
Immediately:
- Rotate the token/key/password
- Revoke the old one in the service
- Update
.envwith new value
# Option A: Remove file completely
git filter-branch --tree-filter 'rm -f .env' HEAD
git push --force
# Option B: Edit history (smaller files)
git filter-branch --tree-filter 'sed -i "s/old_secret/NEW_SECRET/g" .env' HEAD
git push --force- Tell team members to pull the updated history
- Ask them to reset their
.envfiles - Verify no one cached the old secret
- Check where the old secret might be logged
- Monitor for unusual activity on that account
- Watch GitHub security alerts
.env
.env.local
.env.*.local
data/
logs/
Stores hash of "approved" or "test" secrets that are safe to have in code (e.g., placeholder tokens in docs). This file should be reviewed carefully.
Use for CI/CD:
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}- Use
.env.exampleas a template - Add helpful comments to
.env.example - Keep secrets in environment variables
- Rotate secrets regularly
- Use GitHub Secrets for CI/CD
- Hardcode secrets in code
- Commit
.envfiles - Share secrets via email/chat
- Use the same secret in multiple places
- Leave debug tokens in production
To test the hooks are working:
# Create a test file with a fake secret
echo "DISCORD_TOKEN=fake_token_12345" > test_secret.ts
# Try to commit it
git add test_secret.ts
git commit -m "test"
# Should be blocked by pre-commit hook ✅
# Fix: Remove the secret and try againFor questions about secrets management:
- Check this guide
- Review
.github/workflows/secrets.ymlfor what's scanned - Ask team lead before committing sensitive data
- When in doubt, assume it should NOT be committed