How do I stop pasting API keys into Cursor and Claude Code?

Agents are extremely good at reading whatever is in front of them. That includes the .env file, the terminal output and the error message you pasted with the key still in it.

August 3, 2026 · The Squiid team · 6 min read

Short answer: keep secrets in a gitignored env file, tell the agent it may never read or print that file, never paste a key into a chat window, and reduce the number of keys that exist at all. The last one does more than the first three combined, because a key that does not exist on your machine cannot be leaked from your machine.

The problem is not that agents are malicious. It is that a coding agent's entire job is to read your project and act on it, and a secret sitting in your project is just more text. It gets read into context, quoted back in explanations, echoed in a command, written into a committed config file when the agent "fixes" an environment issue, and captured in the screenshot you post when you ask for help.

Where keys actually leak

  • Into the chat. You paste a stack trace to ask what is wrong, and the trace contains the request headers.
  • Into the repository. The agent creates a config file with the value inlined because the environment variable was not loading, and that file is not in .gitignore.
  • Into your shell history. An export line typed once lives in .zsh_history forever.
  • Into a preview deployment. Production keys copied into a preview environment that anyone with the URL can reach.
  • Into a screenshot or a screen share. The most common one, and the hardest to detect afterwards.
  • Into a third party MCP server or extension. Anything you connect that can read files can read the env file.

Assume every one of these has already happened at least once on any project older than a month. That is not a reason for despair, it is a reason to design for it: short-lived, scoped, and few.

The four rules

1. One env file, gitignored, never opened by the agent. Keep every secret in .env.local or .env, add it to .gitignore before you put anything in it, and commit a .env.example with the variable names and empty values so the agent knows what exists without seeing what it is.

2. Say it out loud in the project instructions. Agents follow written project rules well. Put a line in CLAUDE.md, .cursorrules or whatever your tool reads: never read, print, echo or commit .env files; refer to variables by name only. This stops the common accident where the agent cats the file to debug a config error.

3. Never paste a key or a raw error into a chat window. Redact before you paste. If you have pasted one, treat it as public and rotate it, which should take about five minutes.

4. Scope and limit what each key can do. A read-only key for a read-only job. A test mode key in development. A separate key per environment so revoking preview access does not take production down.

The concrete setup

Both Claude Code and Cursor read ignore files and project instruction files, and both have settings for restricting what the agent may touch. The exact option names move between releases, so check your tool's current docs rather than trusting a blog post, including this one. The shape of the configuration is stable even when the names are not:

# .gitignore
.env
.env.local
.env.*.local

# .cursorignore  (and the equivalent ignore file for your agent)
.env
.env.local
*.pem
secrets/

Then a rule in your project instruction file:

# CLAUDE.md
## Secrets
- Never read, cat, print or commit .env files.
- Refer to secrets by variable name only, for example process.env.SQUIID_API_KEY.
- If a command would print a secret, pipe it through a redaction step or do not run it.

Also turn on whatever permission prompts your agent offers for reading files outside the working directory and for running shell commands. Autonomy is lovely until an agent decides to debug your credentials.

What scoping looks like in practice

Rule four is the one people nod at and skip, so here is what it means service by service. A database gives you an anonymous key that respects row level security and a service role key that ignores it entirely; your application code should almost never hold the second one, and your agent should never see it. An email provider usually lets you create a sending-only key restricted to one domain, which is far less useful to an attacker than a full access key that can also read your contact lists. An object store can issue a key limited to one bucket and to write operations. A model provider can often issue a key with its own spending limit, separate from the account balance.

Do this once per service and the worst case stops being catastrophic. The pattern to aim for is a key that can do exactly the one job the code in front of it does, and nothing else, so that a leak is an inconvenience rather than an incident.

What to do about the keys you already pasted

Rotate them, in this order: anything that can spend money, anything that can send email or SMS as your domain, anything that can read user data, then the rest. A model API key that leaked is a bill. An email key that leaked is a phishing campaign from your domain. A database service role key that leaked is every user record you hold.

Then check whether the provider offers a usage log with IP addresses, and look for calls you cannot account for. Most do. This takes ten minutes and is the difference between "probably fine" and actually knowing.

Secret managers: useful, and not quite the answer

1Password, Doppler, Infisical, HashiCorp Vault and the cloud providers' own secret stores all solve a real problem: they stop secrets living in plain files and give you a single place to rotate and audit them. If you have a team, use one.

They do not solve the agent problem, though, and it is worth being clear about why. A secret manager injects the secret into the process environment at run time. Once it is there, it is in the environment the agent can read, print and send. The manager moved where the key is stored; it did not reduce how many provider keys your project handles or how long each one lives. That is why the list in your env file is still eight variables long.

The fix that actually scales: have fewer keys

Every rule above is damage control around a bad shape: a small project holding eight long-lived credentials that each grant unlimited spending at a different vendor. The structural fix is to hold one credential that is not a provider credential.

That is the shape Squiid uses. Squiid holds the upstream provider keys in custody and your project carries a single SQUIID_API_KEY. Requests go to the gateway at https://api.squiid.io/v1/<service>/ and Squiid attaches the real credential on the way through, so the Supabase service role key, the Resend key and the Twilio auth token never exist on your laptop, in your repository, in a preview deployment or in an agent's context. Your .env is one line.

The blast radius changes accordingly. A leaked Squiid key is bounded by your prepaid credit balance and revoked with one click, and rotating it does not mean visiting six consoles or redeploying six services. It is also auditable in one place: every call an agent made, per service, per day. None of that removes the need for the four rules. It just means that when they fail, which they will, what leaked was one revocable key with a spending ceiling rather than permanent access to your users' data.

Questions people ask

Is it safe to put API keys in a .env file?

It is the normal practice and it is fine, provided the file is in .gitignore before any value goes into it, your agent is told never to read or print it, and the keys inside are scoped and rotatable. The risk is not the file format, it is long-lived keys with unlimited permissions.

What should I do if I pasted an API key into a chat window?

Treat it as public and rotate it immediately, starting with anything that can spend money or send email as your domain. Then check the provider usage log for calls you do not recognise. Rotation on most providers takes under five minutes.

Does a secret manager stop agents from seeing my keys?

No. A secret manager controls where secrets are stored and who can fetch them, but it still injects the value into the process environment at run time, where an agent with shell access can read it. It reduces storage risk, not exposure to your own tooling.

One key your agent can see.

Squiid holds the provider credentials and gives your project one revocable key with a spending ceiling. Your .env file becomes one line.