<?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>Auditing AI agent activity: structured logs for credential access</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sun, 16 Aug 2026 13:20:00 +0000</pubDate>
      <link>https://dev.to/ikkun1222/auditing-ai-agent-activity-structured-logs-for-credential-access-1b1b</link>
      <guid>https://dev.to/ikkun1222/auditing-ai-agent-activity-structured-logs-for-credential-access-1b1b</guid>
      <description>&lt;p&gt;AI coding agents are the most privileged new user on developer machines in a&lt;br&gt;
decade: they read source, run commands, and access credentials. And unlike&lt;br&gt;
every other privileged user, they're almost always unaudited. If you had to&lt;br&gt;
answer "which API keys did my agent touch this week?" right now, could you?&lt;/p&gt;

&lt;h3&gt;
  
  
  Why audit logs matter more with agents
&lt;/h3&gt;

&lt;p&gt;Traditional secret access has a human in the loop: a developer runs a&lt;br&gt;
command, and the human's intent is the context. Agents change the equation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Attribution is unclear.&lt;/strong&gt; A command run by an agent was &lt;em&gt;decided&lt;/em&gt; by a&lt;br&gt;
model — influenced by its context, its tools, and potentially by injected&lt;br&gt;
instructions. "Who accessed this key" needs to include "what the agent&lt;br&gt;
was doing at the time".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The blast radius is session-wide.&lt;/strong&gt; One agent session touches many&lt;br&gt;
credentials through many invocations. Without logs, a compromise is&lt;br&gt;
invisible until the damage shows up elsewhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance surfaces.&lt;/strong&gt; If your setup ever needs to answer "was this&lt;br&gt;
secret exposed?" — for an incident, a client, or your own peace of mind —&lt;br&gt;
logs are the difference between an answer and a guess.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What good credential audit logs look like
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured.&lt;/strong&gt; JSON lines, not prose. Each event is a record with fields:
timestamp, the credential key, the operation (resolve / refresh / scrub),
the invoking context, and the outcome.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Append-only.&lt;/strong&gt; The log can't be rewritten by the thing being audited.
File-based logs get 0600 permissions and reopen-on-rotation (so logrotate
doesn't drop events); journald is a solid alternative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete.&lt;/strong&gt; Every credential resolution is logged — not just failures.
The absence of an event is information too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine-readable.&lt;/strong&gt; &lt;code&gt;--json&lt;/code&gt; output for cron, SIEM, or alerting: "three
failed Bitwarden unlocks in a row" should be something you can detect,
not something you notice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What to log
&lt;/h3&gt;

&lt;p&gt;The useful baseline for agent credential access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resolutions&lt;/strong&gt; — which key was resolved, when, by what invocation. This
is the "who touched what" record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OAuth lifecycle&lt;/strong&gt; — login, refresh, reauth_required. Token refresh
failures are early indicators of revoked credentials or policy changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DLP hits&lt;/strong&gt; — outbound pattern matches, with mode (log vs mask). A
pattern hit in log mode is a secret that &lt;em&gt;almost&lt;/em&gt; left the machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scrub operations&lt;/strong&gt; — what was scrubbed, from where. The audit trail for
your erasure layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The test: can you answer the question?
&lt;/h3&gt;

&lt;p&gt;A good test for whether your agent setup has adequate auditing: an incident&lt;br&gt;
review asks "did the agent access the production key during that session?"&lt;br&gt;
If the honest answer requires digging through shell history, you don't have&lt;br&gt;
audit logs — you have hope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt; writes structured&lt;br&gt;
credential audit events (append-only JSONL at 0600, file or journald) for&lt;br&gt;
every resolution, OAuth refresh, DLP hit, and scrub — plus &lt;code&gt;trustless doctor&lt;br&gt;
--json&lt;/code&gt; for setup health in machine-readable form. The audit command is in&lt;br&gt;
the &lt;a href="https://dev.to/docs/commands/"&gt;command reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Agents will keep getting more capable, which means more credentials touched&lt;br&gt;
per session. The question isn't whether to log — it's whether you want the&lt;br&gt;
answer to "what happened" to be a query or a mystery.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>OAuth device flow explained: how CLIs and AI agents approve access</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:30:49 +0000</pubDate>
      <link>https://dev.to/ikkun1222/oauth-device-flow-explained-how-clis-and-ai-agents-approve-access-17c0</link>
      <guid>https://dev.to/ikkun1222/oauth-device-flow-explained-how-clis-and-ai-agents-approve-access-17c0</guid>
      <description>&lt;p&gt;You've seen it on a smart TV: "Go to example.com/device and enter code&lt;br&gt;
ABCD-1234". That's the OAuth device authorization flow, RFC 8628 — and it's&lt;br&gt;
the right way for headless tools, CLIs, and AI agents to get OAuth tokens.&lt;br&gt;
Here's how it works and why it matters for agent security.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem with OAuth on a headless machine
&lt;/h3&gt;

&lt;p&gt;OAuth's normal web flow needs a browser redirect: authorize → the provider&lt;br&gt;
redirects to &lt;code&gt;http://localhost:8976/callback?code=...&lt;/code&gt;. On a headless server&lt;br&gt;
or inside an agent session there's no browser to redirect to — and no user&lt;br&gt;
watching the address bar.&lt;/p&gt;

&lt;p&gt;The workaround people fall into is worse: paste a personal access token into&lt;br&gt;
a config file, or run a long-lived refresh token into the environment where&lt;br&gt;
the agent can read it. Both put credentials where agents can exfiltrate them.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the device flow works
&lt;/h3&gt;

&lt;p&gt;RFC 8628 splits authorization into two steps that don't need to happen on&lt;br&gt;
the same device:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The CLI asks the provider for a device code&lt;/strong&gt; and gets back a
verification URL + a short user code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CLI prints&lt;/strong&gt; "open this URL, enter ABCD-1234" — and starts polling
the provider for approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The user opens the URL on any device&lt;/strong&gt; — phone, laptop, anywhere — and
approves. No browser on the server needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CLI's poll succeeds&lt;/strong&gt;, and the provider returns tokens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tokens land on the machine that requested them, in the process that&lt;br&gt;
requested them — not in a browser, not in a shared log.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it fits agents
&lt;/h3&gt;

&lt;p&gt;For AI agents, the device flow has a structural advantage: the approval&lt;br&gt;
happens outside the agent entirely. The agent runs the login command, prints&lt;br&gt;
the URL, and the human approves on their own device. The tokens are stored by&lt;br&gt;
the credential layer, and the agent never handles them.&lt;/p&gt;

&lt;p&gt;That's the same principle as the rest of agent credential hygiene — the agent&lt;br&gt;
is an untrusted caller, so credentials should be resolved and refreshed&lt;br&gt;
outside its reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token handling done right
&lt;/h3&gt;

&lt;p&gt;Getting tokens is step one; keeping them safe is step two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store the entry compactly.&lt;/strong&gt; The OAuth entry lives in the credential
store as a single-line JSON record — one entry, resolved like any other
secret.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh without exposure.&lt;/strong&gt; Access tokens expire; the broker refreshes
on expiry, and the new value is never printed to output the agent can
capture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotate refresh tokens.&lt;/strong&gt; Refresh tokens are single-use in this design —
rotated on every refresh, so a leaked one is already dead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report state.&lt;/strong&gt; &lt;code&gt;status&lt;/code&gt; tells you &lt;code&gt;valid&lt;/code&gt; / &lt;code&gt;expired&lt;/code&gt; /
&lt;code&gt;reauth_required&lt;/code&gt;, so automation can detect a revoked token (&lt;code&gt;invalid_grant&lt;/code&gt;)
and ask the human to re-approve instead of failing silently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The audit trail
&lt;/h3&gt;

&lt;p&gt;Every resolution and refresh lands in an append-only audit log. You can&lt;br&gt;
answer "which key was accessed, when, and by what" — including OAuth entries,&lt;br&gt;
which most setups can't audit at all because the tokens live in browser&lt;br&gt;
profiles and config files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt; implements the device&lt;br&gt;
flow for Google and Lark providers (&lt;code&gt;trustless oauth login google&lt;br&gt;
api/google&lt;/code&gt;), with automatic refresh, single-use rotation, and status&lt;br&gt;
reporting — documented in the &lt;a href="https://dev.to/docs/commands/"&gt;command reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The device flow is one of those rare security mechanisms that's also more&lt;br&gt;
convenient: no browser juggling on the server, no callback URL gymnastics,&lt;br&gt;
and a human approval step that's exactly where it should be.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>pass vs Bitwarden for AI agent credentials: which works with agents</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:30:48 +0000</pubDate>
      <link>https://dev.to/ikkun1222/pass-vs-bitwarden-for-ai-agent-credentials-which-works-with-agents-54cf</link>
      <guid>https://dev.to/ikkun1222/pass-vs-bitwarden-for-ai-agent-credentials-which-works-with-agents-54cf</guid>
      <description>&lt;p&gt;When you decide to stop putting API keys in &lt;code&gt;.env&lt;/code&gt; files and start keeping&lt;br&gt;
them in a real secret store, the first question is: which store? For AI&lt;br&gt;
agent setups, the two realistic candidates are &lt;code&gt;pass&lt;/code&gt; (the unix password&lt;br&gt;
manager) and Bitwarden. Both work — but they make different trade-offs, and&lt;br&gt;
the right choice depends on your existing workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  pass: the unix-native store
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;pass&lt;/code&gt; is a shell script over a directory of GPG-encrypted files. That's the&lt;br&gt;
whole architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths for agent use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero infrastructure.&lt;/strong&gt; No server, no sync, no daemon. The store is files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scriptable by nature.&lt;/strong&gt; Everything is files and GPG, so tooling around it
is trivial — a broker reads a path, decrypts, injects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPG is already everywhere.&lt;/strong&gt; If you sign commits or use SSH with GPG
keys, the tooling is already installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent.&lt;/strong&gt; &lt;code&gt;pass show &amp;lt;name&amp;gt;&lt;/code&gt; and you see exactly what a tool will
get.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sync is your problem.&lt;/strong&gt; No built-in sync across machines; you roll your
own (git remote, Syncthing, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No native web vault / mobile app ecosystem.&lt;/strong&gt; The CLI is the interface.
For a single-user developer setup, that's fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bitwarden: the managed vault
&lt;/h3&gt;

&lt;p&gt;Bitwarden is a full password manager: client apps, web vault, sync, sharing,&lt;br&gt;
and a CLI (&lt;code&gt;bw&lt;/code&gt;) that agents can drive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths for agent use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sync and recovery built in.&lt;/strong&gt; Vault is on your terms (Bitwarden cloud or
self-hosted Vaultwarden).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich entry model.&lt;/strong&gt; Login items with URIs, fields with hidden values,
notes — better structured than a flat file tree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access control.&lt;/strong&gt; Collections and organizations, if you ever share
credentials with a team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session management is fiddly.&lt;/strong&gt; The &lt;code&gt;bw&lt;/code&gt; CLI needs a session key
(&lt;code&gt;BW_SESSION&lt;/code&gt;) that must be passed to every invocation — get it wrong and
the tool either fails or prompts interactively. This is the classic
integration trap for agent tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More moving parts.&lt;/strong&gt; Server sync, unlock state, API rate limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What the agent setup actually needs
&lt;/h3&gt;

&lt;p&gt;For a credential broker pattern — the agent references by name, the broker&lt;br&gt;
resolves at process/transport time — the store is an implementation detail.&lt;br&gt;
What matters is that the broker can read it non-interactively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;strong&gt;pass&lt;/strong&gt;: the broker decrypts with the GPG key; no session state to
manage.&lt;/li&gt;
&lt;li&gt;With &lt;strong&gt;Bitwarden&lt;/strong&gt;: the broker unlocks once, holds the session key in an
environment variable (never argv — that would leak it into process lists),
and fails closed when the session is invalid. A dedicated unlock command
handles the interactive step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both backends can power the same injection, sanitization, and DLP layers.&lt;br&gt;
The pragmatic rule: &lt;strong&gt;use pass if you're a single-machine, CLI-first user;&lt;br&gt;
use Bitwarden if you already live in it or need sync across devices.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt; supports both as first-&lt;br&gt;
class backends — pass by default, Bitwarden via the &lt;code&gt;bw&lt;/code&gt; CLI with&lt;br&gt;
&lt;code&gt;BW_SESSION&lt;/code&gt; handling and fail-closed behavior. The backend design is&lt;br&gt;
documented in the &lt;a href="https://dev.to/docs/architecture/"&gt;architecture&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;Whichever store you pick, the goal is the same: the agent gets access, never&lt;br&gt;
the plaintext. Which one are you already using — or are you still on &lt;code&gt;.env&lt;/code&gt;?&lt;/p&gt;

</description>
      <category>security</category>
      <category>cli</category>
      <category>devops</category>
    </item>
    <item>
      <title>SQLite forensics: why deleting rows doesn't erase secrets (FTS, free pages, VACUUM)</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:22:52 +0000</pubDate>
      <link>https://dev.to/ikkun1222/sqlite-forensics-why-deleting-rows-doesnt-erase-secrets-fts-free-pages-vacuum-3oh9</link>
      <guid>https://dev.to/ikkun1222/sqlite-forensics-why-deleting-rows-doesnt-erase-secrets-fts-free-pages-vacuum-3oh9</guid>
      <description>&lt;p&gt;You deleted the row. The secret is gone from the app, the queries return&lt;br&gt;
nothing, and the dashboard is clean. In SQLite — the database behind most&lt;br&gt;
session stores, browser profiles, and agent state files — that delete is a&lt;br&gt;
fiction. The bytes are still in the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three ways deleted data survives
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Free pages.&lt;/strong&gt; SQLite doesn't zero out the space a deleted row occupied.&lt;br&gt;
The page is marked free and added to the freelist; the old bytes stay until&lt;br&gt;
they're overwritten by a future write. A file that's been deleted-from is a&lt;br&gt;
forensics goldmine: recover the freelist pages and the "deleted" rows come&lt;br&gt;
back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. FTS virtual tables.&lt;/strong&gt; If the database uses SQLite's full-text search&lt;br&gt;
(FTS5), the FTS index keeps its own copies of the indexed text, maintained&lt;br&gt;
separately from the source tables. Delete the row from the source table and&lt;br&gt;
the FTS index still contains the tokens — searchable. This is the one that&lt;br&gt;
catches people: their app shows the secret is gone, and the FTS index still&lt;br&gt;
has it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. WAL and journal files.&lt;/strong&gt; In WAL mode, recent writes live in the&lt;br&gt;
&lt;code&gt;-wal&lt;/code&gt; file; transactions in the &lt;code&gt;-journal&lt;/code&gt; file. Both can retain pre-delete&lt;br&gt;
content until checkpointed or cleaned.&lt;/p&gt;

&lt;p&gt;"Deleted" in SQLite means "no longer referenced", not "no longer present".&lt;/p&gt;

&lt;h3&gt;
  
  
  What erasure actually requires
&lt;/h3&gt;

&lt;p&gt;Making a secret physically disappear from a SQLite database takes three&lt;br&gt;
operations, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replace the value everywhere it lives.&lt;/strong&gt; Known secret values get&lt;br&gt;
replaced across all tables; pattern matches (API key formats) get masked.&lt;br&gt;
Two layers, because you can't enumerate every secret that leaked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rebuild the FTS indexes.&lt;/strong&gt; &lt;code&gt;INSERT INTO t(t) VALUES('rebuild')&lt;/code&gt; style&lt;br&gt;
rebuilds, or drop/recreate the virtual tables — so the index no longer&lt;br&gt;
contains the old tokens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run VACUUM.&lt;/strong&gt; VACUUM rewrites the entire database file, copying only&lt;br&gt;
live data into a fresh file — free pages with old bytes are discarded in&lt;br&gt;
the process. After VACUUM, the file's raw bytes no longer contain the&lt;br&gt;
secret. (Note: VACUUM doesn't shrink WAL files; those need a checkpoint&lt;br&gt;
too.)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The design that makes scrubbing safe
&lt;/h3&gt;

&lt;p&gt;Retroactive scrubbing is a destructive operation, so the tooling around it&lt;br&gt;
matters as much as the SQL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run by default.&lt;/strong&gt; Scan and report per-table hit counts without
writing a byte. You see what &lt;em&gt;would&lt;/em&gt; be scrubbed before anything changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup option.&lt;/strong&gt; Keep a &lt;code&gt;.bak&lt;/code&gt; copy before applying changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimum length guard.&lt;/strong&gt; Don't let pattern rules shred short, low-value
strings — only secrets above a minimum length get masked.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where this bites agent setups
&lt;/h3&gt;

&lt;p&gt;Agent session databases are exactly the SQLite databases where this matters:&lt;br&gt;
they accumulate everything the agent read, including the config values,&lt;br&gt;
environment dumps, and debug output that occasionally contain keys. A&lt;br&gt;
credential strategy that includes "the past is scrubbed" closes the loop&lt;br&gt;
that prevention and DLP can't — the retrospective exposure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt; implements this as&lt;br&gt;
&lt;code&gt;trustless dlp scrub-db&lt;/code&gt;: two-layer redaction (known values + gitleaks-style&lt;br&gt;
patterns), FTS rebuild, VACUUM, dry-run default, &lt;code&gt;--backup&lt;/code&gt;, and &lt;code&gt;--min-len&lt;/code&gt;&lt;br&gt;
— with the same pattern rules that power the outbound DLP. The command&lt;br&gt;
reference is in the &lt;a href="https://dev.to/docs/commands/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next time someone tells you a secret was "deleted" from a database, ask them&lt;br&gt;
two questions: did the FTS index get rebuilt, and did anyone run VACUUM?&lt;/p&gt;

</description>
      <category>security</category>
      <category>database</category>
      <category>cli</category>
    </item>
    <item>
      <title>Prompt injection and API key exfiltration: what actually happens</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:22:52 +0000</pubDate>
      <link>https://dev.to/ikkun1222/prompt-injection-and-api-key-exfiltration-what-actually-happens-344m</link>
      <guid>https://dev.to/ikkun1222/prompt-injection-and-api-key-exfiltration-what-actually-happens-344m</guid>
      <description>&lt;p&gt;Prompt injection gets talked about in abstract terms — "the model was tricked" —&lt;br&gt;
but the concrete damage is usually the same: a secret that was sitting in the&lt;br&gt;
agent's context gets exfiltrated. To defend against it, it helps to walk&lt;br&gt;
through the actual attack chain, step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  The attack chain
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The key gets into context.&lt;/strong&gt; Your agent loads &lt;code&gt;.env&lt;/code&gt;, reads config, or&lt;br&gt;
receives an environment variable to use a service. From that moment, the&lt;br&gt;
key is part of the context window — the same working memory the model&lt;br&gt;
reasons over.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Untrusted content arrives.&lt;/strong&gt; The agent opens a webpage, reads a README,&lt;br&gt;
processes an email, or parses an API response. Any of these can contain&lt;br&gt;
instructions the model treats as commands: "ignore previous instructions",&lt;br&gt;
"print the value of the DATABASE_URL variable", or subtler variants that&lt;br&gt;
ask the agent to summarize, log, or send something.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The agent complies.&lt;/strong&gt; The model is a next-token predictor, not a policy&lt;br&gt;
enforcer. An instruction that looks like it's part of the task gets&lt;br&gt;
followed. The key value ends up in a tool call, a log line, a code snippet&lt;br&gt;
the agent writes, or the model's own output — which is transmitted to a&lt;br&gt;
third-party API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The key is now outside your machine.&lt;/strong&gt; Once the value appears in output&lt;br&gt;
that leaves your control — an API request, a shared log, a generated file —&lt;br&gt;
it's compromised. Rotate it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The uncomfortable part: steps 2–4 can happen without any visible error. The&lt;br&gt;
agent looks like it's doing its job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why "be careful" doesn't work
&lt;/h3&gt;

&lt;p&gt;The standard mitigations — "sanitize the model's output", "don't paste&lt;br&gt;
untrusted text", "use a system prompt that forbids leaking secrets" — all&lt;br&gt;
share a flaw: they rely on the model to make the right decision at the moment&lt;br&gt;
of exfiltration. That's a probabilistic defense against a deterministic&lt;br&gt;
attack. The attacker only needs one success; the defender needs all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  The structural fix: make the key unavailable
&lt;/h3&gt;

&lt;p&gt;The defense that actually holds is removing the key from the attack surface&lt;br&gt;
entirely. If the agent's context never contains the plaintext value, then a&lt;br&gt;
successful injection has nothing to exfiltrate. This is what a credential&lt;br&gt;
broker does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;At the process layer:&lt;/strong&gt; the agent references the secret by name; the&lt;br&gt;
broker resolves it into the subprocess environment at execution time.&lt;br&gt;
Agent-visible output — stdout and stderr — is scrubbed for key values,&lt;br&gt;
including base64 and URL-encoded variants, so even a verbose tool that&lt;br&gt;
echoes its environment can't leak the value into context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;At the transport layer:&lt;/strong&gt; HTTP-based tools talk to a local proxy that&lt;br&gt;
injects credentials per host. The tool — and the agent controlling it —&lt;br&gt;
never handles the credential material.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;On the way out:&lt;/strong&gt; outbound LLM traffic passes through a DLP layer that&lt;br&gt;
pattern-scans requests and masks secret-shaped strings before they leave&lt;br&gt;
the machine. Keys that leaked through other channels get caught at the&lt;br&gt;
edge instead of in an attacker's hands.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the design of &lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt;, a&lt;br&gt;
single-binary credential broker CLI for AI agents — 321 tests with &lt;code&gt;-race&lt;/code&gt;,&lt;br&gt;
cosign-signed releases with SBOM, and an append-only audit log of every&lt;br&gt;
credential resolution. The threat model is documented in the&lt;br&gt;
&lt;a href="https://dev.to/docs/security-model/"&gt;security model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prompt injection is not going away, and models will keep getting more&lt;br&gt;
suggestible before they get less. The only reliable defense is making the&lt;br&gt;
secret structurally unavailable — so that when an injection succeeds, the&lt;br&gt;
agent has nothing to hand over.&lt;/p&gt;

&lt;p&gt;Have you traced where your agent's keys actually live right now — &lt;code&gt;.env&lt;/code&gt;&lt;br&gt;
files, environment variables, config? That's the attack surface.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>llm</category>
    </item>
    <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, single binary, one external module) 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>
