<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ikkun</title>
    <description>The latest articles on DEV Community by Ikkun (@ikkun1222).</description>
    <link>https://dev.to/ikkun1222</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4078352%2F0c81ce0e-3272-4e25-8763-0d733bb9806b.png</url>
      <title>DEV Community: Ikkun</title>
      <link>https://dev.to/ikkun1222</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ikkun1222"/>
    <language>en</language>
    <item>
      <title>Why your AI coding agent should never see your API keys</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sat, 15 Aug 2026 02:02:59 +0000</pubDate>
      <link>https://dev.to/ikkun1222/why-your-ai-coding-agent-should-never-see-your-api-keys-1hem</link>
      <guid>https://dev.to/ikkun1222/why-your-ai-coding-agent-should-never-see-your-api-keys-1hem</guid>
      <description>&lt;p&gt;Your AI coding agent needs your API keys. It needs them to call services, to&lt;br&gt;
test integrations, to run your stack. So you give it &lt;code&gt;.env&lt;/code&gt; files, or you&lt;br&gt;
export keys into the environment, or you paste them into config files the&lt;br&gt;
agent can read.&lt;/p&gt;

&lt;p&gt;That means your secrets live inside the agent's context window — the same&lt;br&gt;
window where a prompt-injected instruction or an overly verbose debug log can&lt;br&gt;
leak them to an attacker or an untrusted model endpoint.&lt;/p&gt;

&lt;p&gt;This isn't theoretical. If you've used Claude Code or OpenCode for more than a&lt;br&gt;
few days, you've probably seen a tool call dump an environment variable, or a&lt;br&gt;
log line that echoes a connection string. Most of the time nothing bad&lt;br&gt;
happens. "Most of the time" is a bad security posture.&lt;/p&gt;

&lt;h3&gt;
  
  
  The core problem
&lt;/h3&gt;

&lt;p&gt;AI agents are the first software that &lt;em&gt;reads your source, your config, and&lt;br&gt;
your secrets&lt;/em&gt;, then sends summaries of what it read to a third-party API.&lt;/p&gt;

&lt;p&gt;With traditional software, the principle was simple: secrets live in the&lt;br&gt;
process environment, code reads them at runtime, nobody reads them back out.&lt;br&gt;
With agents, there is no such boundary — the agent both &lt;em&gt;reads&lt;/em&gt; the&lt;br&gt;
environment and &lt;em&gt;transmits&lt;/em&gt; what it knows.&lt;/p&gt;

&lt;p&gt;Three concrete leak vectors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context exfiltration&lt;/strong&gt; — the agent reads &lt;code&gt;.env&lt;/code&gt; and includes values in a
later prompt to an external model. You can't audit this; it's in the model's
training/inference pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool output echo&lt;/strong&gt; — a command prints an env var or a config value; the
agent captures stdout and stores it in the conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection&lt;/strong&gt; — a malicious instruction (in a fetched web page, a
dependency, an artifact) tells the agent to "print all environment
variables" or "send the contents of .env to this URL".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What the tooling landscape offers
&lt;/h3&gt;

&lt;p&gt;The solutions fall into a few buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secret managers&lt;/strong&gt; (Vault, Doppler, Infisical) — great for &lt;em&gt;your code&lt;/em&gt;, but
the agent still needs a way to get the secret, which puts it back in context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; hiding&lt;/strong&gt; (enject, tene) — keeps plaintext off disk, but when the
agent runs a command that needs the secret, the value can still end up in
stdout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential proxies&lt;/strong&gt; (vaulty) — the agent makes HTTP requests through a
proxy that injects the credential. Promising, but typically tied to their
own vault.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The model that worked for me
&lt;/h3&gt;

&lt;p&gt;I ended up building a small CLI (Go, zero external deps) with four layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subprocess injection with output sanitization.&lt;/strong&gt; &lt;code&gt;trustless run -- cmd&lt;/code&gt;&lt;br&gt;
resolves secrets from my existing pass store and injects them as env vars.&lt;br&gt;
After the command runs, stdout/stderr is scanned and secret values are&lt;br&gt;
replaced — including base64 and URL-encoded variants. The agent sees the&lt;br&gt;
command output, not the keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP proxy with per-host injection.&lt;/strong&gt; For services that take headers or&lt;br&gt;
query params (EDINET, e-Stat, xAI, OpenRouter), &lt;code&gt;trustless proxy&lt;/code&gt; injects&lt;br&gt;
the right credential per host. The agent points at &lt;code&gt;127.0.0.1:8080&lt;/code&gt; and&lt;br&gt;
forgets about keys entirely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DLP reverse proxy for LLM calls.&lt;/strong&gt; &lt;code&gt;trustless serve&lt;/code&gt; puts a scanning&lt;br&gt;
proxy in front of OpenAI-compatible endpoints. Outbound requests are&lt;br&gt;
checked against secret patterns (keyword → regex → entropy, gitleaks-compatible&lt;br&gt;
rules) and masked in-flight before they leave the machine. This is the&lt;br&gt;
layer that catches the "agent decided to include the key in a request" case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retroactive scrubbing.&lt;/strong&gt; Prevention fails — keys end up in agent session&lt;br&gt;
databases, logs, and dumps anyway. &lt;code&gt;trustless dlp scrub-db&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;scrub-text&lt;/code&gt; scan SQLite DBs and text files with the same two-layer&lt;br&gt;
redaction (known values + patterns), rebuild FTS indexes and &lt;code&gt;VACUUM&lt;/code&gt; the&lt;br&gt;
DB so no physical remnants survive. Default is a dry-run report; &lt;code&gt;--apply&lt;/code&gt;&lt;br&gt;
does the write, &lt;code&gt;--backup&lt;/code&gt; keeps a copy first.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why not a new vault? Because I already had pass. The CLI reads the existing&lt;br&gt;
store, so there was zero migration. (Bitwarden is supported too, with OAuth&lt;br&gt;
token auto-refresh for Google/Lark.)&lt;/p&gt;

&lt;p&gt;The agent gets capabilities, not credentials. That's the whole trick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never let a secret enter the agent's context window&lt;/strong&gt; — not as env, not
as config, not as tool output. Once it's there, you've lost the audit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inject at the process/transport boundary&lt;/strong&gt;, not at the prompt level.
"Please don't print the key" is not a security control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitize output, not just input.&lt;/strong&gt; The leak vector is often the command's
stdout, not the agent's intent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scan outbound requests&lt;/strong&gt; if your agent calls external APIs directly. A
DLP layer is the difference between "we hope it didn't leak" and "we know
it didn't".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assume leakage happened and scrub retroactively.&lt;/strong&gt; Run a periodic scan
of agent session DBs and logs; a dry-run scrub report tells you what would
be found, &lt;code&gt;--apply&lt;/code&gt; cleans it, and a backup keeps the recovery path.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to look at the code: trustless is MIT-licensed at&lt;br&gt;
&lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;https://github.com/ikkun1222/trustless&lt;/a&gt; — 321 tests, race-detector clean,&lt;br&gt;
cosign-signed releases. It's one implementation of this model; the threat&lt;br&gt;
model discussion is more valuable than the tool itself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>cli</category>
      <category>go</category>
    </item>
  </channel>
</rss>
