AI coding agents run with your full access, so any secret you can read, they can read, and anything they print can sit in a chat log forever. Learn a simple, practical way to give an agent the API keys, database passwords, and tokens it needs without those secrets ever leaking into a transcript.

When this is handled carelessly, the fallout is real: a leaked API key or database password can let someone reach production systems, run up large cloud bills, or quietly read customer data long after the conversation is closed, and the only true fix is to rotate every secret that was exposed. AI coding assistants can now run commands, call APIs, and connect to databases on your behalf. To do that, they often need a secret: an API key, a token, a database password. The natural instinct is to paste it into the chat or let the agent read it out of a file. It feels harmless because it works.
The problem is where that text ends up. Everything an agent runs, and everything it prints, is captured in the conversation transcript. That transcript is saved, frequently synced to the cloud, and kept as history. A password that flashes on screen for a single second is now stored in a place you cannot easily reach. The only real fix is to rotate the secret, and that is only if you noticed.
This playbook lays out a short list of habits that let you give an agent the secrets it needs while keeping those secrets out of the transcript entirely.
"Encrypt your secrets at rest" is good advice, and you should still do it. But it solves a different problem. Encryption at rest protects you if someone steals your laptop or pokes around your files. It does nothing against the AI agent itself, because the agent is running as you. It has your login, your keys, and your active sessions. Any secret you can unlock, it can unlock too.
So the real question is not "is the file encrypted." It is "could the plaintext ever end up in the conversation." That small shift in thinking is the foundation for everything else.
The one rule The plaintext secret must never appear in the output of any command the agent runs. Every habit in this playbook follows from that single idea.
Two ideas do most of the work. First, holding a secret and using a secret are different moments. Store it locked away. Unlock it only at the instant something needs it, inside the tool that uses it, and let it disappear right after. There should never be a separate "show me the decrypted value" step, because that step is exactly what the transcript captures.
Second, the agent should only ever handle a label, not the value. Think of a coat check. The agent carries the ticket, hands it to the counter, and the actual coat stays behind the counter the whole time. The agent never holds the coat itself.
Pick the lowest rung that fits how sensitive the secret is and whether it needs to be shared.
| Where it lives | Good for | Pros | Cons |
|---|---|---|---|
A gitignored .env file | Non-secrets only: local ports, feature flags, public URLs | Simple, no tooling | Plaintext on disk; never safe for real secrets |
| Your OS secret store (Windows DPAPI, macOS Keychain, Linux libsecret) | The default for real secrets on your own machine | Free, built in, no tooling; encrypted and tied to your user | Local only, cannot be shared |
| A certificate in the OS store | Logins where the service accepts a certificate instead of a secret string | No shared secret string; strong per-person identity | More setup; only some services support it |
| A shared vault (Azure Key Vault, 1Password) | Anything the whole team needs | One copy, per-person access, audit trail, central rotation | Needs a service and setup; small cost |
No stored secret at all (sign in as yourself: az login, gh auth) | The best option when the service supports it | Nothing to store or leak; per-person identity | Only where identity auth is supported |
Caution A plaintext.envfile is fine for a localhost port number, but it is never the place for a client secret, a database password, or an API key, even if it is gitignored. If it would hurt to see it in a screenshare, it does not belong in.env.
You do not need to buy anything to do this well. Every major operating system ships with an encrypted secret store, and it is the right place to start: Windows DPAPI, macOS Keychain, and Linux libsecret. They are free, built in, need no extra tooling, and tie each secret to your user account on your machine. For most developers, the OS store is the whole solution.
The shape is the same everywhere. Keep one small encrypted store that holds many secrets, each saved under a plain name like "billing-api" or "reporting-db." A tool asks for a secret by that name, and the store hands the value back in memory, only to the tool that asked. That is all a local "vault" really is: one locked drawer, many labeled envelopes, opened only at the moment of use.
Connecting this to an agentic coding platform is mostly discipline, not plumbing. Two pieces do the work:
When the team needs to share a secret, the architecture does not change, only the backend does. You move the same named secrets into a shared vault such as Azure Key Vault or 1Password, and your tools resolve the shared vault first, falling back to the local store so a half-migrated team keeps working. Nothing else has to move. You gain one copy, per-person access, an audit trail, and central rotation, while every tool keeps asking for the same names it always did. Start free on the local store, and reach for a paid vault only when sharing actually forces the question.
The concepts are the easy part. Here is what actually happens when a secret is stored and fetched, and how to stand it up on your own machine in a few minutes, at no cost.
On Windows, the encryption is done by DPAPI, the built-in Data Protection API. When you save a secret, the operating system encrypts it with a key derived from your user account. Only that account, on that machine, can decrypt it. There is nothing to install, no master password to manage, and no service to run. macOS does the same thing through the Keychain, and Linux through libsecret.
The vault itself is just one small encrypted file. Decrypted in memory, it holds a tiny map of names to values, like { "billing-api": "...", "reporting-db": "..." }. Saving a secret means decrypting that map in memory, adding your entry, and re-encrypting the whole map back to the file. Fetching one means decrypting in memory and reading a single entry. DPAPI is what cryptographically binds the file to your account; on top of that, a file-system permission locks the file to you as a second layer.
The encryption itself is just two built-in PowerShell cmdlets. A real helper wraps them around the serialized map, but conceptually the primitive is this:
# store (conceptually): plaintext becomes DPAPI ciphertext, tied to your user
ConvertFrom-SecureString $data > secrets.vault.dat
# use (conceptually): ciphertext becomes plaintext again, only your account can
ConvertTo-SecureString (Get-Content secrets.vault.dat)
The detail that matters is timing. The decryption happens inside the same short-lived process that uses the value. The plaintext exists in memory for a moment and is never written to disk, never printed, and never passed on a command line. That is what keeps it out of the transcript.
The commands below come from a small DPAPI-based helper built for Windows (a single PowerShell script, run with pwsh 7+). Treat them as illustrative rather than a tool you install: they show the shape of the workflow, and any wrapper you write, or your OS store on its own, exposes the same handful of verbs. On macOS the same steps map to security add-generic-password and find-generic-password; on Linux, to secret-tool store and lookup.
1. Store a secret. You run this once, in your own terminal. It prompts with no echo, so the value never appears on screen or in shell history.
secret-vault set billing-api
Secret value for [billing-api]: ******
stored [billing-api] ab12...wxyz (40 chars) - vault now holds 1 secret(s)
Do this yourself, never the agent
Storing a secret is the one step the agent must never run for you. If the agent types or pastes the value, it lands in the transcript, which is exactly what this whole approach avoids. Always run set in your own terminal and enter the value at the no-echo prompt. The agent reads secrets by name afterward; it should never see or set the raw value.
2. Confirm it landed. Listing shows names only, and get shows a fingerprint rather than the value.
secret-vault list
- billing-api
secret-vault get billing-api
billing-api ab12...wxyz (40 chars)
3. Let a tool use it. The tool that needs the secret resolves it and uses it inside its own short-lived process, so the plaintext never reaches the agent's shell or the transcript. Only the result of the call comes back out.
That is the whole lifecycle. To add another secret, repeat step one with a new name. To rotate one, run step one again with the new value. To delete one, secret-vault remove billing-api. This particular helper also ships a scan (a redacted secret sweep) and a doctor (a permissions and hygiene check), but those are extras, not part of the core flow.
Your agent does not need a list of secrets, and you do not want one cluttering its context. It needs a single line in its memory or instructions file, for example:
Secrets live in the local vault. Resolve them by name with the
secret-vault tool, in-process, and never print a value.
That one line is all the agent carries. The names live in your tools and the values live in the encrypted vault, so the context window stays clean and no secret is ever part of the conversation. When a workflow needs billing-api, the agent runs the tool, the value resolves for that one call, and only the result comes back.
This is where secrets actually leak, so these habits matter most.
curl -v or shell tracing dump everything, including auth headers, into the output. Keep them off around anything sensitive.show a fingerprint, not the key: ab12...wxyz (40 chars)
We have a small internal tool that works like a saved Postman collection: it fetches a login token and calls our APIs. The first version worked, but it had a few quiet leaks. The secret and the token were passed on command lines, where another program could read them, and it skipped a security check that should only ever be skipped on your own machine.
The fix was simple in spirit. We rewrote it so a single step unlocks the secret, gets the token, and makes the call all at once, then hands back only the result. The secret and token are never printed, never sit on a command line, and never touch a file. The lesson is general: do the sensitive work in one place, and let only the answer come back out.
Your OS secret store is deliberately tied to one person on one machine. That is a feature, not a limitation. When a secret genuinely needs to be shared, move up one rung to a central vault such as Azure Key Vault with role-based access. That gives you one copy of the secret, access granted per person, a full audit trail, and one place to rotate it.
A stronger option for sensitive systems is to give each developer their own certificate, so there is no shared secret at all. Whatever you do, never send a secret over Slack, email, or a zipped file. If it ever traveled in plaintext, treat it as compromised and rotate it.
You do not need new tooling to start. You need a shared agreement that every tool which touches a secret follows the same rules.
For new team members, the setup is short: use your OS secret store, get access to the shared vault or upload your certificate (never a raw secret handed over in chat), and add a secret-scanning check to your repositories. When someone leaves, remove their vault access, rotate anything they could have seen, and confirm nothing sensitive landed in their branches.
Giving an AI agent access to secrets does not have to be risky. The shift is to stop thinking about secrets as files to encrypt and start thinking about them as values that must never be spoken aloud in the transcript. Store them locked, refer to them by name, unlock them only at the moment of use, and show fingerprints instead of values. Adopt these habits once, write them into a shared contract, and every tool your team builds inherits the same safety for free.
