September 14, 2026
My AI Coding Agent Read an API Key.
The Generated File I Forgot to Block

By developerasun
4 min read
The Generated File I Forgot to Block
Gemini was my main coding agent during the early development of NicheJuice. It worked inside the same Next.js project where I managed the credentials for local development and production.
I thought I had separated those concerns well enough. The credentials lived in dotenv files, local and production used different values, and I had blocked the agent from reading the obvious secret files.
Then a generated deployment file slipped through.
I asked Gemini whether it had just read a secret-bearing file generated during deployment.
It said yes.
I followed up: had the file stayed inside my terminal, or had its contents been sent to Google's servers? Gemini told me that the tool result, including the file contents, had been sent to Google's servers so it could produce the response.
The exchange that made the exposure clear.
The file contained API secrets.
My rules covered the secret files I knew about, but not every artifact that a build or deployment tool might create later. The generated file was inside a directory Gemini could read, so the agent read it.
Dotenv had not suddenly become the problem. The problem was that production secrets existed inside the same working environment that the agent could inspect.
Rotation Came First
Once I confirmed the exposure, I checked which secrets were affected and rotated them.
Rotation invalidates an existing secret and replaces it with a new value. Most of the services I was using provided a way to do this from their management interface. I could investigate the cause afterward. The exposed values could not remain valid while I did it.
I also checked the production database. Luckily, it only contained one registered account at the time: mine. I found no user impact.
Once I had contained the immediate risk and understood the scope of the exposure, I turned to the larger problem: the setup that had allowed a coding agent to reach production secrets.
Would adding this directory to the deny list be enough?
One More Access Rule Wasn't Enough
Adding the generated deployment directory to the restricted paths would close the gap I had found. It would not protect me from the next generated file I did not know about.
A Next.js deployment can produce files and directories through several layers of tooling. Those outputs can change when the framework, adapter, or deployment process changes. A deny list based on the files I already recognize depends on me predicting every future place where a secret might appear.
I was not going to solve the problem by giving up coding agents. They had become part of how I built software, and Gemini had already played a large role in building NicheJuice. I now use Codex as my main coding agent, but changing the tool would not change the structure that allowed the exposure.
I changed the goal instead.
I could keep improving the access rules, but they could no longer be the final boundary. If an agent read an unexpected file again, the mistake should stop before it reached production.
I Moved Production Secrets Off My Development Laptop
I thought back to how the teams I had worked with handled secrets.
Some projects encrypted secret files so that reading the file was not enough to reveal its contents. Others used secret management services from cloud platforms such as AWS or GitHub. In some environments, production secrets lived on separate hardware that developers reached through SSH.
I combined parts of the second and third approaches for NicheJuice.
First, I issued separate credentials for local development and production. My development laptop now keeps only the local values.
Second, I moved the production secrets to separate hardware. I configured SSH access to that machine, alongside the VPS infrastructure I already managed. The production values are retrieved only during deployment and then stored using the cloud platform's encryption.
The diagram below shows what changed. It focuses on the trust boundaries rather than the provider names or deployment commands.
An architectural comparison of the environments, with operational details intentionally omitted.
The important difference is not another entry in a blocklist. The production credentials no longer sit inside the environment I use for everyday AI-assisted development.
An agent can still make a bad tool call. I can still miss a generated directory. Separating the credentials reduces what that mistake can reach.
A Security Model Small Enough for One Developer
A larger team can operate centralized secret stores, narrowly scoped permissions, automatic rotation, and audit logs. Reproducing that entire system for a small solo project could create more operational work than I can maintain.
Keeping every secret on my laptop would be easier, but it would also let one file-access mistake become a production incident.
The setup I chose sits between those two extremes. It is not a universal security architecture, and separate hardware plus SSH creates responsibilities of its own. It is a boundary I can maintain while working alone.
If you use an AI agent to build and deploy a web service, these are the checks I would start with:
- Do your access restrictions cover directories created by build and deployment tools?
- Do local development and production use different credentials?
- Are production secrets stored on the machine where your coding agent works?
- Can you revoke and replace a credential as soon as exposure is suspected?
- If the agent reads an unexpected file, what prevents that mistake from reaching production?
I initially missed one generated file. The more serious problem was that the file could lead back to production credentials.
Trusting or distrusting the agent does not change that path. The useful boundary is the one that remains in place when the agent, or I, makes a mistake.
The goal is not just to prevent mistakes, but to build an environment where those mistakes cannot reach production.
Originally published at https://nichejuice.com on September 14, 2026.