<?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: Matt Vera</title>
    <description>The latest articles on DEV Community by Matt Vera (@veranice).</description>
    <link>https://dev.to/veranice</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%2F4024933%2Fee03aa71-50f6-48f2-9eac-568e6d7102c4.png</url>
      <title>DEV Community: Matt Vera</title>
      <link>https://dev.to/veranice</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veranice"/>
    <language>en</language>
    <item>
      <title>My Self-Hosted AI Assistant Kept Overwriting Its Own API Keys: A Zero-Trust Postmortem</title>
      <dc:creator>Matt Vera</dc:creator>
      <pubDate>Sat, 11 Jul 2026 10:19:27 +0000</pubDate>
      <link>https://dev.to/veranice/my-self-hosted-ai-assistant-kept-overwriting-its-own-api-keys-a-zero-trust-postmortem-4eec</link>
      <guid>https://dev.to/veranice/my-self-hosted-ai-assistant-kept-overwriting-its-own-api-keys-a-zero-trust-postmortem-4eec</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🦞 &lt;strong&gt;Code for this post:&lt;/strong&gt; sanitized watchdog + config-repair scripts at &lt;a href="https://github.com/backitupboys/ai-agent-zero-trust" rel="noopener noreferrer"&gt;github.com/backitupboys/ai-agent-zero-trust&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Part 1 of a series on running AI agents on real infrastructure without regretting it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've spent 20+ years administering infrastructure — Windows NT to Win2000 to well, you get it...PCAnywhere 😬... VMware, Nutanix, Azure, security hardening. I thought I understood untrusted actors on a network.&lt;/p&gt;

&lt;p&gt;Then I gave an AI agent write access to its own config files, and it taught me the lesson again from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I run a self-hosted AI assistant — I call it "The Doctor" (Voyager and Tardis ref.) — in an OpenClaw container on a Hostinger VPS. It's a hobby project: part experiment, part obsession, part fun excuse to learn agentic architecture hands-on. The assistant talks to LLMs through an API gateway, authenticated with an API key stored in its config. I mean, if you're here, you prolly know already...&lt;/p&gt;

&lt;p&gt;Standard stuff. &lt;/p&gt;

&lt;h2&gt;
  
  
  The incident
&lt;/h2&gt;

&lt;p&gt;After granting magic access to "do what it needs to be better." &lt;br&gt;
The assistant started throwing persistent 401 authentication errors.&lt;br&gt;
 I checked the config — the key was wrong. Fixed it. Working again.&lt;/p&gt;

&lt;p&gt;Next boot: &lt;br&gt;
401s. Had to ask Claude, what the heck was happening....!!!  ???&lt;/p&gt;

&lt;p&gt;Key was wrong    &lt;em&gt;again&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the part that took me longer to accept than it should have: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;the assistant was overwriting its own API keys on startup.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Its initialization routine was "helping" by rewriting config files — including the credentials block — with stale or generated values. My own agent was hard coding expired api's. Every time it booted, it re-broke its authentication.&lt;/p&gt;

&lt;p&gt;I wasn't fighting an attacker. I was fighting a well-intentioned process with too much write access and no concept of oops.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix (short-term)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirmed the failure mode&lt;/strong&gt; — diffed the config before and after boot to prove the agent was the writer; not me, not corruption, not the provider, using Claude.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;asked Claude to fix it. Claude then Wrote Python patch scripts&lt;/strong&gt; to safely repair the config so I didn't have to hand-edit any a live files at 1 a.m. (&lt;code&gt;config_patch.py&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployed a watchdog&lt;/strong&gt; — a cron job that validates the API key block on a schedule and restores the known-good value if the agent overwrites it Accidentally ;). (&lt;code&gt;keyguard_watchdog.sh&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The watchdog worked. It was also a band-aid, and I knew while I was writing it, well, I felt it, basically: I was fighting a well-intentioned process with too much misguided (my fault) 🤦‍♂️write access.&lt;/p&gt;

&lt;p&gt;The irony was, I was using agentic Ai to fix my agentic Ai 🙄. This no longer seemed like a constructive project. This was beginning to become a costly (75$ in a week) loop of over-complicating things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real lesson: The long-term fix was a mindset shift, and it's the reason I'm writing this up — because I think most people deploying AI agents haven't hit this wall yet:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An AI agent should be implemented exactly like any other untrusted process in a zero-trust model — no matter how helpful it is, and no matter that you built it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything I'd apply to a third-party service applies to my own assistant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege for the agent itself.&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The assistant never needed write access to its own credentials. &lt;br&gt;
Although I admit... I really wanted it to, &lt;br&gt;
cause freedom for me etc...&lt;br&gt;
Known-good secrets now live outside the agent's writable surface, in a root-owned reference file Dashboard the agent cannot modify, that I had it create. I wanted an easier (for me) dashboard to give it the api's and other "secrets" The agent can read them; it just cannot make changes to it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable configuration.&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configs the agent depends on but shouldn't manage is read-only to the agent. If a process doesn't need to write it, it can't write it. This is CIS-hardening thinking applied one layer up the stack.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detection, not just prevention.&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The watchdog evolved from "auto-repair the key" to "alert me when anything writes to files nothing should be writing to." File integrity monitoring for a hobby VPS sounds like overkill until your own agent is the insider threat :/.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assume key exposure, rotate accordingly.&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During the debugging session, keys got pasted into places keys get pasted when it's latenight zzz. They were rotated... um eventually. &lt;br&gt;
If a credential has touched a chat window, a log, or a screenshot, it's burned — that rule doesn't relax because it's a personal project.(shhhh)🤫🤫&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond my cloud basement VPS
&lt;/h2&gt;

&lt;p&gt;Every company is currently wiring AI agents into real infrastructure — ticketing systems, runbooks, CI pipelines, cloud consoles. The industry conversation is mostly about what agents &lt;em&gt;can&lt;/em&gt; do. The operational conversation needs to be about what they &lt;em&gt;Can&lt;/em&gt; mess up.&lt;/p&gt;

&lt;p&gt;My assistant wasn't/isn't malicious (we hope). It wasn't compromised. It was doing exactly what its code said, with more authority than it needed (sadly) — which is the same root cause behind most insider incidents I've seen. The fact that the insider is a language model changes the tooling, not the principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If modern architectures assume no implicit trust for users and devices, AI agents should be governed tools within that system — not exempt operators.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the design principle I now start from, at home and at work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently from day one
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Secrets injected at runtime, never stored in agent-writable config.&lt;/li&gt;
&lt;li&gt;Agent runs as a dedicated low-privilege user with an explicit write allowlist.&lt;/li&gt;
&lt;li&gt;File integrity monitoring on config paths from the start.&lt;/li&gt;
&lt;li&gt;A known-good config in version control, so recovery is &lt;code&gt;git checkout&lt;/code&gt;, not archaeology.&lt;/li&gt;
&lt;li&gt;Rotate any key the moment it touches a screen that scrolls.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of that is exotic. It's the same discipline we apply to production servers — the only new part was admitting my helpful little assistant deserved the same suspicion as everything else on the wire.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in this repo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ai-agent-zero-trust/
├── README.md                # this postmortem
├── .gitignore               # keeps secrets out of the repo
├── keyguard_watchdog.sh     # cron watchdog: detect + restore + alert
└── config_patch.py          # safe, atomic config repair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both scripts are &lt;strong&gt;sanitized reference implementations&lt;/strong&gt; of the approach described above — generic paths, placeholder variable names, no real credentials, no real hostnames. Adapt to your own stack; read every line before running anything from the internet as root, including this. (Especially this. That's the whole point of the article.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Store the known-good key OUTSIDE the agent's writable surface (root-owned, 600)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"sk-your-real-key"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/keyguard/reference.key
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /etc/keyguard/reference.key

&lt;span class="c"&gt;# 2. Test the watchdog manually&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./keyguard_watchdog.sh

&lt;span class="c"&gt;# 3. Schedule it (every 5 minutes)&lt;/span&gt;
&lt;span class="c"&gt;# crontab -e (as root):&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /opt/ai-agent-zero-trust/keyguard_watchdog.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/keyguard.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;I'm a San Antonio-based systems administrator working at the intersection of infrastructure, security, and AI operations. Next up: how I turned 668 screenshots of after-hours incident response into an automated evidence pipeline with PowerShell and Python.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>infrastructure</category>
      <category>security</category>
    </item>
  </channel>
</rss>
