AI Agent Secret Hygiene: Never Put Keys in Rules
The first rule of AI agent secret hygiene is simple: rules hold policy text only—never literal keys, tokens, or passwords. Project .mdc files under .cursor/rules, root AGENTS.md, and shared “team rules” are versioned, shared, and injected into context. A literal secret there leaks through the repo, transcripts, and PR diffs.
This post covers only what must never go in rules, where local-only values live (.env, keychain, allowlist), and a leak checklist. No real secret values, pricing, plans, personal reviews, or invented product click-paths. Grounded in Cursor Rules, Ignore file, Cloud Agent Secrets & Network, and Cloud Agent setup.
What must never go in rules?
One-line answer: Any reusable credential literal—API keys, tokens, passwords, DB URLs with embedded secrets, private key material, session cookies—and fragments that reconstruct them. Rules state prohibitions, not values.
Project Rules are committed .mdc files. Whether alwaysApply or globs, once content is in context it can follow into chat, tool output, and commit messages. A key inside a rule is not documentation; it is an exfiltration vector.
Do not put (names only—never sample values)
| Kind | Why it hurts | Put this in rules instead |
|---|---|---|
| API keys / Bearer tokens | Survive in index, PRs, logs | NEVER literals; reference process.env / Secret names |
| DB/broker URLs with passwords | One connection string is enough | Role of the host only; password in env |
.pem / private key bodies | Often unrecoverable | Path and permission policy (chmod 600) |
| Internal webhook / bot tokens | Re-emitted in chat | “Name only; value in Cloud/local Secrets” |
| Customer PII / real data samples | Policy and contract risk | Synthetic fixtures and masking rules |
OK to put
- Prohibitions —
NEVERwrite secrets as literals; never commit.env*. - Env var names — e.g.
STRIPE_API_KEY(name only). - Where values live —
.env.local/ OS keychain / Cursor Secrets tab. - Path policy — do not commit or
@-mentionsecrets/,*.pem. - Habits — scan
git diffbefore commit; prefer a secret-scan hook.
---
description: Secret hygiene (no literal credentials)
alwaysApply: true
---
# Secrets
- NEVER paste API keys, tokens, passwords, or private keys into rules, AGENTS.md, code, commits, or chat.
- Read credentials from environment variables or the OS/cloud secret store. Refer by **name** only.
- Do not commit `.env`, `.env.*`, `*.pem`, `credentials.json`, or `secrets/`.
- If a secret appears in a diff or tool output, stop, redact, rotate, and continue without the value.
That block is policy. Do not add fake key-string “examples”—they train the model to emit lookalikes.
User Rules and Team Rules follow the same rule. Team Rules land in every teammate’s context, so a personal token there spreads to every team session.
Where do local-only values live?
One-line answer: Keep values in local .env (gitignored), OS keychain/secret manager, Cloud Agent Secrets, and (for cloud) network allowlists. The repo keeps names and load instructions only. .cursorignore is convenience, not a security boundary.
Local Agent
| Place | Role | Caveat |
|---|---|---|
.env / .env.local | Values apps/scripts read | Must be gitignored. Default ignore includes .env*—accidental commits are separate |
| OS keychain / secret manager | Runtime lookup by CLI/SDK | Do not paste export output into chat |
| Shell environment | Session-scoped injection | May appear in history, screenshots, agent terminal logs |
.cursorignore | Softens indexing / Agent file access | Docs: terminal and MCP can still read ignored files |
Cursor respects .gitignore and a default list (including .env*) for indexing. Add more sensitive paths in .cursorignore if needed. Per the Ignore file docs, Agent terminal and MCP tools are not guaranteed the same block. Real control is permissions, a secret manager, and never storing values in the repo.
Tell the agent:
- “Do not read key values—confirm variable names and load paths only.”
- “
.env.examplegets placeholders only—never real values.” - “If you are about to write a literal into config, stop and switch to env references.”
Cloud Agent
Do not rely on a laptop .env in the cloud. Put values in the dashboard Secrets tab (setup). Types (Secrets & Network):
| Type | Use for | Visible to the agent? |
|---|---|---|
| Environment Variable | Non-sensitive flags / public URLs | Value visible (still encrypted at rest) |
| Runtime Secret | Sensitive API credentials | Redacted as [REDACTED] in tools, transcript, commits |
| Build Secret | Image build step only | Not in the running agent env |
Prefer Runtime Secret for sensitive keys. A human on the agent’s terminal may still see them—ban “print the secret” in rules. After add/change, start a new agent so injection applies.
Prefer short-lived OIDC over long-lived cloud keys when you can. For egress risk, use Cloud Agent network mode + allowlist and only the hosts you need—docs warn against broad *.s3… wildcards that open exfiltration paths.
What stays in the repo / what does not
Keep: .env.example (names only), Secret name list, a short “local = keychain, CI = OIDC” note, NEVER lines in rules.
Do not keep: real values, private keys, production connection strings, screenshots of export KEY=….
What is the leak checklist?
One-line answer: Walk commit/PR → rules/chat → local ignore → cloud secrets → rotate. If a value appeared, revoke and reissue first.
Before commit / PR
git status/git diffhas no.env,*.pem,credentials.json,secrets/.- Diff has no live credential-shaped material (do not invent sample strings here).
- Commit message, PR body, and screenshots have no tokens.
- Pre-commit / secret-scan hook passes if you use one.
Rules and prompts
- No literal secrets in
.cursor/rules/**,AGENTS.md, User/Team Rules. - No “example key” strings left as teaching aids.
- Only names and NEVER policy remain.
Local and ignore
.env*is in.gitignore. If already tracked:git rm --cached, then rotate.- Add sensitive paths to
.cursorignoreif needed—operate assuming terminal/MCP bypass. - Do not ask the agent to
cat .envor “print the key.”
Cloud Agent
- Sensitive values are Runtime Secret (or equivalent). If a snapshot baked
.env.local, move to the Secrets tab. - After secret changes, start a new run.
- If on Allowlist mode, destinations are minimal—no broad
*.s3…entries. - Chat/PR shows
[REDACTED], not raw values.
If something leaked
- Rotate — revoke/reissue beats deleting logs.
- List every place it was pasted (gist, issue, Slack).
- Add one prevention NEVER line to rules (still no value).
A weekly pass over items 1–7 stops most rules-channel leaks; add 11–14 when you use Cloud Agents.
What should you remember?
Rules = policy; secret stores = values. Keep NEVER lines and env names in .mdc/AGENTS.md; keep values in .env, keychain, Cloud Secrets (Runtime Secret when sensitive), and tight allowlists. .cursorignore helps indexing—it is not a security boundary. Rotate first when a value appears. Skills-vs-Rules and permission modes live elsewhere; this post is only do not put keys in rules.
Sources
- Cursor Rules — project rules and version control
- Ignore file —
.cursorignore, default.env*, terminal/MCP limits - Cloud Agent Secrets & Network — Environment / Runtime / Build Secrets and allowlists
- Cloud Agent setup — Secrets tab vs snapshots and
.env - LLM Safety and Controls — ignore is convenience; permissions and encryption are the real defense