The Agent Secrets Pattern: Giving AI Access Without Git Exposure

I stumbled on this pattern while building automation commands for Claude Code and Cursor.
The problem: my AI agents needed credentials for browser automation, but I obviously couldn't commit those credentials to git.
The best practice for keys and credentials is .env, or environment variables, but using that would create a new problem.
My app's .env has database URLs, API keys, and other runtime secrets. Mixing automation credentials in there felt off. And more practically, the .env lived in a subproject directory, which made the path awkward for workspace-level commands.
Cursor added another layer of complexity because it can't read .env files directly.
I needed a better solution.
The Pattern: A Dedicated .agent-secrets File
project/
├── .agent-secrets.example # Template (committed)
├── .agent-secrets # Your credentials (gitignored)
├── .gitignore # Blocks .agent-secrets from git
├── .cursorignore # Allows .agent-secrets for Cursor
└── .claude/
└── settings.local.json # Grants explicit read access for Claude Code
The key insight: both Claude Code and Cursor need explicit configuration to read gitignored files.
By default, they respect .gitignore for security. When you need to, you can grant access to specific files.
Configuring Claude Code
Claude Code respects .gitignore for search operations, but it can read files directly if you grant explicit permission.
Add this to .claude/settings.local.json:
{
"permissions": {
"allow": [
"Read(.agent-secrets)"
]
}
}
Now Claude Code can read .agent-secrets without prompting, while the file stays out of your commits.
Configuring Cursor
Cursor blocks access to gitignored files by default. This is a security feature. It also has global ignore patterns that block .env files automatically.
To allow Cursor to read .agent-secrets, create a .cursorignore file with a negation pattern:
# .cursorignore
# Allow agent secrets (overrides gitignore)
!.agent-secrets
The ! prefix tells Cursor to include this file even though it's in .gitignore.
Without this, Cursor's Agent, Tab, and Inline Edit features won't have access to your credentials.
The File Format
Keep it simple. No JSON, no YAML. Just key-value pairs:
# .agent-secrets.example
# Agent Automation Secrets
# Copy to .agent-secrets and fill in values
# LinkedIn Browser Automation
LINKEDIN_EMAIL=
LINKEDIN_PASSWORD=
# Other browser automation credentials can go here
The .example file gets committed. The actual .agent-secrets file with your credentials stays local.
Why Separation Matters
This isn't just about organization.
It's about the principle of least privilege applied to AI agents.
Your app's .env might contain:
- Database connection strings
- Payment processor keys
- Admin API tokens
Your agent doesn't need any of that to log into LinkedIn and scrape a profile.
By separating agent credentials, you:
- Reduce attack surface: If an agent leaks context, it only has automation credentials
- Simplify auditing: One file, one purpose, easy to review
- Enable workspace-level access: Credentials live at project root, not buried in subprojects
Command Integration
In your custom commands, reference the file explicitly:
### Step 3: Login Check
If LinkedIn shows a login wall, read credentials:
\`\`\`bash
cat .agent-secrets 2>/dev/null | grep -E "LINKEDIN_EMAIL|LINKEDIN_PASSWORD"
\`\`\`
Or use ReadFile on `.agent-secrets` directly.
The agent knows exactly where to look. No searching, no guessing.
The Broader Pattern
This approach aligns with established security practices:
| Principle | Application |
|---|---|
| Centralized Management | One file for all agent credentials |
| Environment Segmentation | App secrets vs agent secrets |
| Least Privilege | Agents only access what they need |
| Defense in Depth | Gitignored AND explicitly scoped |
For larger teams or production environments, you'd layer this with proper secret management (HashiCorp Vault, AWS Secrets Manager).
But for solo developers and small teams, the pattern provides meaningful security without operational overhead.
The Decision Framework
Before adding credentials to .agent-secrets, ask:
- Does the agent actually need this? Only include what's required.
- Could this be a read-only token? Prefer minimal permissions.
- Is this documented? Keep
.exampleupdated. - Is there a fallback? "Ask user to log in manually" if credentials are missing.
Implementation Checklist
# 1. Create the template
touch .agent-secrets.example
# Add your variable names (no values)
# 2. Create your actual file
cp .agent-secrets.example .agent-secrets
# Fill in your credentials
# 3. Gitignore it
echo ".agent-secrets" >> .gitignore
# 4. Grant Claude Code access (if using)
# Add Read(.agent-secrets) to .claude/settings.local.json
# 5. Grant Cursor access (if using)
echo "!.agent-secrets" >> .cursorignore
# 6. Document it
# Add section to CLAUDE.md or README
If you're navigating the intersection of AI tooling and security, I'd love to hear your approach.

