‹ all posts

Erik Rekola · updated 2026-10-02

Reducing secret exposure in coding-agent workflows

2026-07-12

Ways to reduce secret exposure when coding agents work with a repository, including credential storage and the permissions around runtime access.

Coding agents now run with your shell. They read your files and run your build. They push commits under your name. That is the point of them. It also means every plaintext secret on your disk is readable by the agent, and by every backup or synced folder that copies your working directory. A token in a text file was a small risk when only you could read it. It is a larger one the moment something else is holding the keyboard.

This is the question an agent-readiness audit asks about your product, turned inward. If you care how a third party exposes data to an agent, your own machine is the first place to get it right.

Here is the posture I would defend, the reasoning behind it, and one Windows trap that cost me an afternoon.

Keep secrets out of plaintext files

The old habit is a token in a dotfile, a key in .npmrc, an unencrypted service account JSON sitting next to the code. It works because the file is only yours. An agent breaks that assumption. So does a leaked backup, a synced folder, or someone watching a screen-share.

Move every secret into storage the operating system encrypts and scopes to your account. On Windows that is the Data Protection API. On macOS the Keychain. On Linux libsecret through the Secret Service, whose backend usually encrypts the value at rest. The specification leaves access control to the implementation, so check what your keyring does when the machine is locked and which applications it lets read the value. Your scripts ask for the secret when they run instead of reading it off disk.

Git credentials through a credential manager

Most people still authenticate git with a personal access token pasted into a credentials file. Drop that. Use a credential manager that speaks OAuth, so the token lives in the OS store, refreshes on its own, and never lands in a file you can commit or copy by accident.

One trap to know if you are on Windows and your forge is not GitHub. The common advice is git-credential-oauth with the wincred store. That store writes to Windows Credential Manager, which caps a single entry at 2560 bytes (CRED_MAX_CREDENTIAL_BLOB_SIZE in the CREDENTIAL structure). Some forges issue OAuth tokens well past that, and the write fails with a bare "CredWrite failed" while fetch still works, so nothing looks wrong until you notice every command re-authenticating. Git Credential Manager's Bitbucket provider splits a large token across entries. At Git Credential Manager commit 9c3b25d, its GitLab provider and the generic store path write the token as one entry, so the Windows size limit still applies there. If a self-hosted forge keeps opening a browser prompt on push, a token over the limit is one possible cause.

A small vault for everything else

Credential managers are built to store one username and password per host. They are the wrong shape for an API key you set as an environment variable, or a private key you would rather not keep as a loose file. Some values also run past the size limit above.

For those, a small file based vault does the job. Encrypt each value with the same OS primitive, keep them in one file, and give it a get command. A deploy script then reads the token when it runs, setting the environment variable from that call instead of from a file on disk. The file is encrypted and tied to your account, so a backup or a stray copy exposes nothing.

Two caveats. A vault tied to your OS user cannot be decrypted after a reinstall, so keep an offline backup of anything you cannot regenerate, like a private signing key. And do not write your own crypto here. Call the OS primitive. It is audited, and it is the same mechanism your credential manager already trusts.

The vault only changes where a secret sits at rest. It does not by itself stop an agent from reaching the value, because if the agent can run the script that calls get, it can still see what that command returns or read it from the environment of the process it started. Encrypting the secret and scoping what the agent is allowed to run and access at runtime are two separate controls, and the vault only provides the first one.

Why this matters for buyers

I build this into my own setup because I sell the audit that checks for it. A prospect who asks for an NDA is asking a real question about whether you treat access seriously or leave keys lying around while an agent works next to them. The honest answer shows in how you work, before it shows in any report.

None of this is exotic. It is one habit applied everywhere. The operating system holds the secret, encrypted and scoped to you as far as its keyring backend provides it, and the code asks for it when it needs it. An agent can then do its work in your repo without a key sitting in a plaintext file it can read at rest, which is a narrower claim than saying it never sees a key in the clear.

Frequently asked

Where should secrets live if an AI agent works in your repo?

In storage the operating system encrypts and scopes to your account. The Data Protection API on Windows, the Keychain on macOS, libsecret on Linux, with a Secret Service backend whose encryption and application access you should check. Scripts ask for the secret when they run instead of reading it off disk.

Why is a token in a plain file a bigger risk than it used to be?

Because the file is no longer only yours. A coding agent reads your files and runs your build, and so does every backup or synced folder that copies your working directory. A stray copy or a screen-share exposes the same plaintext.

Why does a git push keep asking for authentication on Windows?

Windows Credential Manager caps a single entry at 2560 bytes and some forges issue OAuth tokens past that. The write fails while fetch still works, so nothing looks wrong until every command re-authenticates. Git Credential Manager's Bitbucket provider splits a large token across entries, but its GitLab provider and generic store write one entry, so a token over the limit can still fail there.

Corrected 2026-10-02. Two sentences claimed more than their sources. The Linux sentence said the Secret Service encrypts the value and only your account can decrypt it. It now says: "On Linux libsecret through the Secret Service, whose backend usually encrypts the value at rest. The specification leaves access control to the implementation, so check what your keyring does when the machine is locked and which applications it lets read the value." The credential manager sentence said splitting a large token across entries is how Git Credential Manager handles it for self-hosted forges. At commit 9c3b25d, only the Bitbucket provider splits. It now says: "Git Credential Manager's Bitbucket provider splits a large token across entries. At Git Credential Manager commit 9c3b25d, its GitLab provider and the generic store path write the token as one entry, so the Windows size limit still applies there. If a self-hosted forge keeps opening a browser prompt on push, a token over the limit is one possible cause." The summary and the frequently asked answer on storage still stated the Linux guarantee without that limit, and both now carry it.