<?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: Oluwafemi</title>
    <description>The latest articles on DEV Community by Oluwafemi (@nightlyhermes).</description>
    <link>https://dev.to/nightlyhermes</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%2F4126325%2F31fed8be-9207-4747-af7b-741f3a785d52.png</url>
      <title>DEV Community: Oluwafemi</title>
      <link>https://dev.to/nightlyhermes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nightlyhermes"/>
    <language>en</language>
    <item>
      <title>Eight coding agents, eight deny-list formats. What each one actually reads.</title>
      <dc:creator>Oluwafemi</dc:creator>
      <pubDate>Tue, 15 Sep 2026 13:41:07 +0000</pubDate>
      <link>https://dev.to/nightlyhermes/eight-coding-agents-eight-deny-list-formats-what-each-one-actually-reads-3egj</link>
      <guid>https://dev.to/nightlyhermes/eight-coding-agents-eight-deny-list-formats-what-each-one-actually-reads-3egj</guid>
      <description>&lt;p&gt;Every coding-agent harness has a deny list. Almost nobody sets it, because each tool wants a different file, a different syntax and a different idea of what "deny" means.&lt;/p&gt;

&lt;p&gt;I went through all of them while building &lt;code&gt;penv guard&lt;/code&gt;. This is the audit, as of September 2026. Use it even if you never install penv.&lt;/p&gt;

&lt;h2&gt;
  
  
  The threat is boring
&lt;/h2&gt;

&lt;p&gt;Your agent runs as you. If you can &lt;code&gt;cat .env&lt;/code&gt;, so can it. It doesn't need to be malicious: a grep for context, an &lt;code&gt;env&lt;/code&gt; while debugging, a stack trace with the environment attached. The value ends up in a transcript on someone else's server.&lt;/p&gt;

&lt;p&gt;A deny rule stops the &lt;em&gt;read&lt;/em&gt;. It does not stop the value leaking once it's in the process. Keep both problems in your head; this post is only about the first one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each harness wants
&lt;/h2&gt;

&lt;p&gt;Two patterns, everywhere: &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.env.*&lt;/code&gt;. Not &lt;code&gt;*.env&lt;/code&gt;, not &lt;code&gt;.env.local&lt;/code&gt; spelled out. The second one is the whole point: a rule on &lt;code&gt;.env&lt;/code&gt; alone does nothing for &lt;code&gt;.env.local&lt;/code&gt;, &lt;code&gt;.env.production&lt;/code&gt;, &lt;code&gt;.env.bak&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.claude/settings.json&lt;/code&gt; (project). Two mechanisms, and you want both.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env.*)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sandbox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"denyRead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./.env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./.env.*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"penv hook claude-code"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;permissions.deny&lt;/code&gt; is checked by the tool layer. &lt;code&gt;sandbox.filesystem.denyRead&lt;/code&gt; is enforced by the OS sandbox on macOS and Linux. Native Windows has no Claude Code sandbox, so only the deny rules and the hook apply there.&lt;/p&gt;

&lt;p&gt;There's also a user-scope file, &lt;code&gt;~/.claude/settings.json&lt;/code&gt;, where you can list env vars to mask with &lt;code&gt;{"name": KEY, "mode": "mask"}&lt;/code&gt;. penv prints that block and tells you to paste it; it does not write outside your repo.&lt;/p&gt;

&lt;p&gt;The hook answers on &lt;strong&gt;stdout&lt;/strong&gt; with &lt;code&gt;permissionDecision: "deny"&lt;/code&gt; and &lt;strong&gt;exit 0&lt;/strong&gt;. Get that wrong and the hook is ignored.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.codex/config.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[sandbox_workspace_write]&lt;/span&gt;
&lt;span class="py"&gt;deny_read&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"**/.env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"**/.env.*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[shell_environment_policy]&lt;/span&gt;
&lt;span class="py"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"core"&lt;/span&gt;
&lt;span class="py"&gt;ignore_default_excludes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Glob, not path. &lt;code&gt;**/&lt;/code&gt; because Codex resolves from the workspace root. The &lt;code&gt;shell_environment_policy&lt;/code&gt; block matters as much as the deny: &lt;code&gt;inherit = "core"&lt;/code&gt; keeps Codex from handing your whole environment to every subprocess.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Two files. &lt;code&gt;.cursor/cli.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read(.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(.env.*)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;.cursor/hooks.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"beforeReadFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"penv hook cursor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"failClosed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"beforeShellExecution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"penv hook cursor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"failClosed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;failClosed: true&lt;/code&gt; is the line that matters. Without it, a hook that crashes is a hook that allows.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Copilot CLI
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.github/copilot/permissions-config.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(**/.env.*)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honest note, and it's in penv's own template comment: Copilot hasn't published a schema for this file. The shape above is best effort until they do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini CLI
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.gemini/settings.json&lt;/code&gt;. Hook on &lt;code&gt;run_shell_command&lt;/code&gt;, &lt;code&gt;PreToolUse&lt;/code&gt;. Same idea as Claude Code, different matcher name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Amp
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.amp/settings.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amp.guardedFiles.allowlist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amp inverts the model: it guards files by default and you &lt;em&gt;allow&lt;/em&gt; exceptions. An empty allowlist means nothing is exempt. penv only writes this file if it's absent, so an allowlist you already curated is left alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cline
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.clinerules/hooks/PreToolUse&lt;/code&gt;. A two-line &lt;code&gt;/bin/sh&lt;/code&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;penv hook cline &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Must be executable. Denies on stderr, exit 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windsurf
&lt;/h3&gt;

&lt;p&gt;File: &lt;code&gt;.windsurf/hooks.json&lt;/code&gt;. Two hooks, &lt;code&gt;pre_run_command&lt;/code&gt; and &lt;code&gt;pre_read_code&lt;/code&gt;, both pointing at &lt;code&gt;penv hook windsurf&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I got wrong the first time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The hook must be a binary, not a script.&lt;/strong&gt; A &lt;code&gt;node hook.js&lt;/code&gt; or &lt;code&gt;python hook.py&lt;/code&gt; fails open when the interpreter is missing or the wrong version. &lt;code&gt;penv hook &amp;lt;harness&amp;gt;&lt;/code&gt; is the penv binary itself; the only script in the whole set is Cline's two-line shim, because that's the only shape Cline accepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deny replies are not uniform.&lt;/strong&gt; Claude Code and Cursor want the decision on stdout with exit 0. Everyone else wants stderr and exit 2. Answer on both streams, or neither, and you're guessing what the harness does with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Empty stdin is the only allow.&lt;/strong&gt; If the hook can't parse the payload but there was something to match on, it refuses. Fail closed, always.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;penv guard&lt;/code&gt; does with all this
&lt;/h2&gt;

&lt;p&gt;It doesn't write eight files onto your machine. It probes for what's installed: a &lt;code&gt;.claude&lt;/code&gt; or &lt;code&gt;~/.claude&lt;/code&gt; folder, a &lt;code&gt;codex&lt;/code&gt; on PATH, and so on for each. Then it writes the guard for those and only those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;penv guard
&lt;span class="go"&gt;HARNESS      INSTALLED  FILE                    STATUS
claude-code  yes        .claude/settings.json   written
codex        yes        .codex/config.toml      written
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;penv guard --check&lt;/code&gt; shows the same table without writing, and exits non-zero if anything is stale, so it works as a CI step. &lt;code&gt;penv guard cursor&lt;/code&gt; targets one. &lt;code&gt;penv guard --all&lt;/code&gt; writes every harness penv knows, for a template repo.&lt;/p&gt;

&lt;p&gt;Merges are per-file, not per-harness. JSON deny lists are unioned with what you already have. TOML and the Cline script are append-unique. Amp is replace-if-absent. Your existing config survives.&lt;/p&gt;

&lt;p&gt;Two invariants are tested on every render: the patterns are exactly &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.env.*&lt;/code&gt;, never a list of filenames; and no rendered guard may contain &lt;code&gt;.env.local&lt;/code&gt; (enumerating filenames is how you miss one) or &lt;code&gt;.env.schema&lt;/code&gt; (the schema is the one file the agent &lt;em&gt;should&lt;/em&gt; read).&lt;/p&gt;

&lt;h2&gt;
  
  
  The part the deny list can't do
&lt;/h2&gt;

&lt;p&gt;Once a value is in your process, no deny rule helps. That's the other half: &lt;code&gt;penv run -- your-app&lt;/code&gt; injects values at exec time and, when it detects an agent driving, masks every secret in the child's stdout and stderr in 14 encodings (raw, hex, base64 at three phases, URL-encoded, JSON-escaped). Different post.&lt;/p&gt;

&lt;p&gt;The security claim penv prints, verbatim, from &lt;code&gt;penv guard --check&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;penv validates your .env, keeps values out of your agent's output, and blocks it from reading the file where its harness allows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Where its harness allows" is doing real work in that sentence. That's this post.&lt;/p&gt;




&lt;p&gt;penv is MIT, one static Rust binary, no account needed for local mode.&lt;/p&gt;

&lt;p&gt;OSS: &lt;a href="https://github.com/penvhq/penvhq" rel="noopener noreferrer"&gt;https://github.com/penvhq/penvhq&lt;/a&gt;&lt;br&gt;
Install: &lt;code&gt;curl -fsSL penv.cloud/install | sh&lt;/code&gt; or &lt;code&gt;npm i -g @penvhq/cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your harness isn't in the list, or one of these shapes is wrong, say so in the comments. The guard folder is data, so a fix is a PR to one &lt;code&gt;guard.toml&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
