<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ai-Jail | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/ai-jail/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/ai-jail/index.xml" rel="self" type="application/rss+xml"/><item><title>AI-Jail: update de segurança, Docker vira opt-in</title><link>https://akitaonrails.github.io/2026/07/25/ai-jail-update-seguranca-docker-opt-in/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/07/25/ai-jail-update-seguranca-docker-opt-in/</guid><pubDate>Sat, 25 Jul 2026 13:00:00 -0300</pubDate><description>&lt;p&gt;Hoje de manhã abriram um issue importante no repositório do &lt;a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener"&gt;ai-jail&lt;/a&gt;: o &lt;a href="https://github.com/akitaonrails/ai-jail/issues/88"target="_blank" rel="noopener"&gt;issue #88&lt;/a&gt;, reportado pelo &lt;a href="https://github.com/mdindoffer"target="_blank" rel="noopener"&gt;@mdindoffer&lt;/a&gt;, com o título &amp;ldquo;Sandbox escape via a docker socket passthrough (effective host root)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;O report procedia, e a correção já está disponível na &lt;strong&gt;v1.16.0&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Atualize pra v1.16.0&lt;span class="hx:absolute hx:-mt-20" id="atualize-pra-v1160"&gt;&lt;/span&gt;
&lt;a href="#atualize-pra-v1160" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Se você usa o ai-jail, o update é o de sempre:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Arch Linux (AUR)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yay -Syu ai-jail-bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Homebrew (macOS / Linux)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew upgrade ai-jail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# crates.io&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cargo install ai-jail --force
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# mise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mise cache clear &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mise upgrade github:akitaonrails/ai-jail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;O que mudou na v1.16.0&lt;span class="hx:absolute hx:-mt-20" id="o-que-mudou-na-v1160"&gt;&lt;/span&gt;
&lt;a href="#o-que-mudou-na-v1160" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Até a v1.15.x, o ai-jail montava o socket do Docker dentro da jaula automaticamente, sempre que &lt;code&gt;/var/run/docker.sock&lt;/code&gt; existia no host. Read-write. Sem aviso, sem flag, sem pedir opinião. Eu documentei isso no README como &amp;ldquo;favorece usabilidade&amp;rdquo;, e era verdade: agente de coding frequentemente precisa rodar um &lt;code&gt;docker compose&lt;/code&gt; pra subir banco de testes, e o passthrough automático poupava configuração.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hoje de manhã abriram um issue importante no repositório do <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a>: o <a href="https://github.com/akitaonrails/ai-jail/issues/88"target="_blank" rel="noopener">issue #88</a>, reportado pelo <a href="https://github.com/mdindoffer"target="_blank" rel="noopener">@mdindoffer</a>, com o título &ldquo;Sandbox escape via a docker socket passthrough (effective host root)&rdquo;.</p>
<p>O report procedia, e a correção já está disponível na <strong>v1.16.0</strong>.</p>
<h2>Atualize pra v1.16.0<span class="hx:absolute hx:-mt-20" id="atualize-pra-v1160"></span>
    <a href="#atualize-pra-v1160" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você usa o ai-jail, o update é o de sempre:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Arch Linux (AUR)</span>
</span></span><span class="line"><span class="cl">yay -Syu ai-jail-bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Homebrew (macOS / Linux)</span>
</span></span><span class="line"><span class="cl">brew update <span class="o">&amp;&amp;</span> brew upgrade ai-jail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># crates.io</span>
</span></span><span class="line"><span class="cl">cargo install ai-jail --force
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># mise</span>
</span></span><span class="line"><span class="cl">mise cache clear <span class="o">&amp;&amp;</span> mise upgrade github:akitaonrails/ai-jail</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>O que mudou na v1.16.0<span class="hx:absolute hx:-mt-20" id="o-que-mudou-na-v1160"></span>
    <a href="#o-que-mudou-na-v1160" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Até a v1.15.x, o ai-jail montava o socket do Docker dentro da jaula automaticamente, sempre que <code>/var/run/docker.sock</code> existia no host. Read-write. Sem aviso, sem flag, sem pedir opinião. Eu documentei isso no README como &ldquo;favorece usabilidade&rdquo;, e era verdade: agente de coding frequentemente precisa rodar um <code>docker compose</code> pra subir banco de testes, e o passthrough automático poupava configuração.</p>
<p>A partir da v1.16.0 o comportamento inverteu:</p>
<ul>
<li>O passthrough do socket ficou <strong>desligado por padrão</strong>. Só entra se você pedir explicitamente com a flag <code>--docker</code> ou com <code>no_docker = false</code> no <code>.ai-jail</code>.</li>
<li>Quando você liga e existe um socket no host, o ai-jail imprime um aviso na largada deixando claro que aquilo equivale a dar root no host pro processo dentro da jaula.</li>
<li>O <code>ai-jail status</code> agora mostra Docker como <code>disabled (default)</code>, pra ninguém achar que ligou sem querer.</li>
<li>Nos modos <code>--lockdown</code> e browser profile o socket nunca entra, como já era antes.</li>
</ul>
<p>Mudança de comportamento, sim, e de propósito. Se o seu workflow depende de Docker dentro da jaula (o meu depende em alguns projetos), o opt-in é uma linha no <code>.ai-jail</code> do projeto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">no_docker</span> <span class="p">=</span> <span class="kc">false</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O nome do campo ficou feio (<code>no_docker = false</code> pra ligar, eu sei), mas configs antigas continuam parseando igual e o <code>--no-docker</code> / <code>no_docker = true</code> funcionam como antes. Só o default mudou. Os detalhes estão nas <a href="https://github.com/akitaonrails/ai-jail/blob/master/releases/v1.16.0.md"target="_blank" rel="noopener">release notes da v1.16.0</a>.</p>
<h2>O que o issue #88 mostrou<span class="hx:absolute hx:-mt-20" id="o-que-o-issue-88-mostrou"></span>
    <a href="#o-que-o-issue-88-mostrou" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O report do mdindoffer é daqueles que todo mantenedor quer receber: resumo preciso, repro em poucas linhas, proposta de correção. A essência:</p>
<p>O ai-jail montava o socket <strong>cru</strong> do Docker dentro da sandbox, read-write. O daemon do Docker roda como root no host. Logo, um agente dentro da jaula podia rodar isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --rm -v /:/host alpine sh -c <span class="s1">&#39;cat /host/etc/shadow&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E pronto. Leitura de qualquer arquivo do host, como root. Isso derrota de uma vez só o <code>$HOME</code> em tmpfs, o <code>--mask</code>, o <code>--deny-path</code> e o Landlock, porque a ação deixa de acontecer dentro da sandbox: acontece no daemon, que vive fora e acima de qualquer namespace que o bwrap criou. O agente nem precisa escapar da jaula quando a jaula tem uma porta que dá direto na sala de máquinas.</p>
<p>Em retrospecto, deixar isso ligado por padrão foi uma falha de design. Eu sabia que o passthrough era &ldquo;perigoso&rdquo; no abstrato, tanto que escrevi isso no README. O que eu não tinha internalizado: perigoso desse jeito, com esse default, já é vulnerabilidade.</p>
<p>O mdindoffer ainda propôs o caminho de hardening definitivo: em vez de montar o socket cru, interpor um proxy filtrado (estilo o <a href="https://github.com/wollomatic/socket-proxy"target="_blank" rel="noopener">wollomatic/socket-proxy</a>) que só aceita bind mounts de caminhos que o agente já pode escrever dentro da jaula. Está no radar pra uma versão futura. Pra fechar o buraco agora, opt-in com aviso explícito resolve o default, que era onde o problema morava.</p>
<h2>A classe de vulnerabilidade: docker.sock é root<span class="hx:absolute hx:-mt-20" id="a-classe-de-vulnerabilidade-dockersock-é-root"></span>
    <a href="#a-classe-de-vulnerabilidade-dockersock-%c3%a9-root" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Essa não é a primeira vez que eu esbarro nessa história, e aposto que também não é a sua. &ldquo;Quem tem acesso ao socket do Docker tem root no host&rdquo; é um dos clássicos da segurança de containers, documentado pela própria Docker na página de <a href="https://docs.docker.com/engine/security/"target="_blank" rel="noopener">daemon attack surface</a>: só usuários confiáveis devem controlar o daemon, porque o Docker permite compartilhar qualquer diretório do host com um container, sem restrição nenhuma de acesso.</p>
<p>O motivo técnico é simples. O <code>dockerd</code> é um daemon que roda como root e obedece comandos que chegam pela API no socket Unix <code>/var/run/docker.sock</code>. A CLI <code>docker</code> é só um cliente dessa API. Quando você pede <code>docker run -v /:/host</code>, quem cria o container e monta o filesystem inteiro do host dentro dele é o daemon, com privilégio total. E processo dentro de container roda como uid 0 por padrão, que o kernel enxerga como uid 0 de verdade (salvo user namespace remapping, que quase ninguém liga). A conta fecha: acesso de escrita no socket equivale a root no host. O grupo <code>docker</code> é sudo sem senha com outro nome.</p>
<h2>A demonstração, na sua máquina<span class="hx:absolute hx:-mt-20" id="a-demonstração-na-sua-máquina"></span>
    <a href="#a-demonstra%c3%a7%c3%a3o-na-sua-m%c3%a1quina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você tem Docker instalado e seu usuário no grupo <code>docker</code>, reproduz agora. Sem sudo, sem explorar bug nenhum:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># confirme que você é um usuário comum</span>
</span></span><span class="line"><span class="cl">$ id
</span></span><span class="line"><span class="cl"><span class="nv">uid</span><span class="o">=</span>1000<span class="o">(</span>akitaonrails<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>1000<span class="o">(</span>akitaonrails<span class="o">)</span> <span class="nv">groups</span><span class="o">=</span>...,docker
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># tente ler o /etc/shadow diretamente: negado, como esperado</span>
</span></span><span class="line"><span class="cl">$ cat /etc/shadow
</span></span><span class="line"><span class="cl">cat: /etc/shadow: Permission denied
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># agora peça pro daemon fazer isso por você</span>
</span></span><span class="line"><span class="cl">$ docker run --rm -v /:/host alpine sh -c <span class="s1">&#39;head -3 /host/etc/shadow&#39;</span>
</span></span><span class="line"><span class="cl">root:<span class="nv">$6</span>$...:...
</span></span><span class="line"><span class="cl">bin:!:...
</span></span><span class="line"><span class="cl">daemon:!:...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E se quiser o pacote completo, uma shell de root no seu próprio host:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ docker run --rm -it -v /:/host alpine chroot /host /bin/bash
</span></span><span class="line"><span class="cl"><span class="c1"># id</span>
</span></span><span class="line"><span class="cl"><span class="nv">uid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span> <span class="nv">groups</span><span class="o">=</span>0<span class="o">(</span>root<span class="o">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nenhum exploit, nenhum 0-day. Você usou a API oficial, do jeito documentado, e saiu de usuário comum pra root em um comando. Era exatamente isso que um agente dentro do ai-jail conseguia fazer até hoje, mesmo com todas as camadas (bwrap, Landlock, seccomp, rlimits) ligadas.</p>
<h2>Por que isso ainda existe?<span class="hx:absolute hx:-mt-20" id="por-que-isso-ainda-existe"></span>
    <a href="#por-que-isso-ainda-existe" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se todo mundo sabe disso há mais de uma década, por que ninguém &ldquo;consertou&rdquo;? A resposta curta: isso é arquitetura, e arquitetura não se resolve com patch.</p>
<ul>
<li>O daemon root com API todo-poderosa foi o design que fez o Docker simples de operar. Autorização granular por requisição existe na forma de <a href="https://docs.docker.com/engine/extend/plugins_authorization/"target="_blank" rel="noopener">authorization plugins</a>, mas é opt-in, chata de configurar, e eu conto nos dedos os setups que já vi usando.</li>
<li>O <a href="https://docs.docker.com/engine/security/userns-remap/"target="_blank" rel="noopener">userns-remap</a> existe desde o Docker 1.10 e mapeia o root do container pra um usuário sem privilégio no host. Vem desligado de fábrica, porque quebra compatibilidade com imagens e volumes que assumem uid 0.</li>
<li>O <a href="https://docs.docker.com/engine/security/rootless/"target="_blank" rel="noopener">rootless mode</a> roda o daemon inteiro como o seu usuário, com o socket em <code>$XDG_RUNTIME_DIR/docker.sock</code>. Funciona, mas tem restrições de rede e storage, e a internet inteira de tutoriais assume o daemon root no caminho clássico.</li>
<li>O <a href="https://podman.io/"target="_blank" rel="noopener">Podman</a> nasceu rootless e sem daemon justamente por causa dessa crítica. Tem uma seção inteira sobre ele logo abaixo.</li>
</ul>
<p>Resumindo: isso vai continuar existindo. Toda ferramenta que monta <code>/var/run/docker.sock</code> dentro de um ambiente &ldquo;pra conveniência&rdquo; abre o mesmo buraco, consciente ou não. CI que monta o socket pra build de imagem, IDE remota, code-server, sandbox de agente de IA (oi, eu), plugin de painel web. O ajuste fica sempre do lado de quem monta o ambiente.</p>
<h2>Curiosidade: isso é tudo, menos novo<span class="hx:absolute hx:-mt-20" id="curiosidade-isso-é-tudo-menos-novo"></span>
    <a href="#curiosidade-isso-%c3%a9-tudo-menos-novo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você me acompanha há algum tempo, essa história toda deve ter dado déjà vu. Em 2023 eu gravei o <a href="/2023/03/02/akitando-139-entendendo-como-containers-funcionam/">[Akitando #139] - Entendendo Como Containers Funcionam</a>, onde eu explico o que um container realmente é: um processo comum do Linux, limitado por cgroups, enganado por namespaces, com as capabilities cortadas. Sem mágica e sem máquina virtual. E olha o que já estava na lista de links daquele episódio: um walkthrough de <a href="https://flast101.github.io/docker-privesc/"target="_blank" rel="noopener">privilege escalation via Docker</a>, demonstrando exatamente o truque do <code>docker run -v /:/host</code>. O buraco que o issue #88 explorou dentro do ai-jail é o mesmo que eu já apontava num video de 2023, e que já era manjado muito antes disso.</p>
<p>Ninguém sabe disso melhor que a Red Hat. O <a href="https://podman.io/"target="_blank" rel="noopener">Podman</a> nasceu lá em 2018 como resposta direta a essa arquitetura: sem daemon central, rootless por padrão. Cada container vira filho direto do seu usuário, via fork-exec, sem um processo todo-poderoso rodando como root intermediando nada. O &ldquo;docker.sock é root&rdquo; simplesmente não existe nesse modelo, porque não existe nem docker.sock, nem daemon, nem root.</p>
<p>E antes que você pergunte: sim, eu uso Podman em algumas coisas e recomendo. Mas ele também não é solução perfeita, e vale entender o porquê.</p>
<p>O que o Podman faz bem:</p>
<ul>
<li><strong>A arquitetura certa.</strong> Daemonless e rootless desde o dia zero. A classe inteira de vulnerabilidade deste artigo perde o sentido.</li>
<li><strong>Compatibilidade de CLI.</strong> <code>alias docker=podman</code> cobre a maioria esmagadora dos comandos do dia a dia. Build, run, push, pull: mesmos comandos, mesmas flags.</li>
<li><strong>Integração com systemd.</strong> Os <a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html"target="_blank" rel="noopener">Quadlets</a> são, na minha opinião, o jeito mais limpo de rodar container como serviço no Linux. O Docker nunca chegou perto disso.</li>
</ul>
<p>E onde ele tropeça:</p>
<ul>
<li><strong>O socket de compatibilidade não é 100%.</strong> O Podman oferece um socket compatível com a API do Docker (o <code>podman.socket</code>), e muita ferramenta funciona em cima dele. Mas &ldquo;muita&rdquo; não é &ldquo;toda&rdquo;: Testcontainers, alguns plugins de IDE, ferramentas de CI mais exóticas tropeçam em diferenças de comportamento. Funciona até o dia em que não funciona, e aí você perde uma tarde debugando.</li>
<li><strong>Compose é cidadão de segunda classe.</strong> O <code>docker compose</code> oficial até conversa com o socket do Podman, e existe o <code>podman-compose</code>, mas nenhum dos dois tem a mesma redondeza do par original. Projeto com compose complicado é onde a migração costuma enroscar.</li>
<li><strong>Rootless tem preço.</strong> A rede rootless (slirp4netns, e hoje o pasta) tem limitações: sem ping por padrão, IP de origem esquisito, throughput menor. Imagem que assume uid 0 e volume com permissão errada pedem ajuste. Nada grave, mas é atrito.</li>
<li><strong>Docker Desktop é um produto.</strong> No macOS e no Windows, o Docker Desktop entrega uma experiência polida que o Podman Desktop ainda está alcançando. Pra muita gente, esse é o único contato com containers que existe.</li>
</ul>
<p>Somando tudo, você chega na resposta de por que o mundo continua no Docker: inércia de ecossistema. Todo tutorial, todo CI, toda imagem de exemplo, todo <code>docker run</code> colado de Stack Overflow assume o daemon root no caminho clássico. O Docker virou o nome da categoria, tipo Bombril. Migrar pro Podman é tecnicamente fácil e politicamente caro: é você contra o conhecimento acumulado da internet inteira.</p>
<p>Um detalhe que importa pra quem usa sandbox: montar o socket do Podman rootless dentro de uma jaula é bem menos catastrófico que montar o do Docker. O &ldquo;daemon&rdquo; equivalente roda como o seu usuário, então um agente mal-intencionado ganharia os seus privilégios, não root. Continua ruim (dá pra sobrescrever o seu <code>~/.ssh</code> com eles, por exemplo), mas é outro campeonato. Mesmo assim, o default do ai-jail continua valendo: não montar socket nenhum, de runtime nenhum. Opt-in é opt-in.</p>
<h2>Boas práticas pra isso não morder você<span class="hx:absolute hx:-mt-20" id="boas-práticas-pra-isso-não-morder-você"></span>
    <a href="#boas-pr%c3%a1ticas-pra-isso-n%c3%a3o-morder-voc%c3%aa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A lista que eu aplico e recomendo:</p>
<ol>
<li><strong>Trate o grupo <code>docker</code> como sudo sem senha.</strong> Antes de adicionar qualquer usuário ou serviço nele, pergunte se você daria sudo irrestrito pra aquilo. É a mesma coisa.</li>
<li><strong>Nunca monte <code>/var/run/docker.sock</code> em ambientes não confiáveis.</strong> Agente de IA, job de CI que roda código de pull request de estranho, container de terceiro. Faça um <code>grep -r docker.sock</code> nos seus docker-compose, manifests de CI e configs de ferramentas. Aparece em mais lugares do que você lembra.</li>
<li><strong>Precisou expor pra algo semiconfiável? Use um socket proxy.</strong> O <a href="https://github.com/wollomatic/socket-proxy"target="_blank" rel="noopener">wollomatic/socket-proxy</a> e o <a href="https://github.com/Tecnativa/docker-socket-proxy"target="_blank" rel="noopener">docker-socket-proxy da Tecnativa</a> ficam entre o cliente e o daemon com allowlist de endpoints e bloqueio de bind mounts arbitrários. Read-only por padrão, você liga só o que precisa.</li>
<li><strong>Prefira rootless sempre que possível.</strong> Podman rootless no Linux é o caminho mais limpo; o rootless mode do próprio Docker é a segunda opção. O daemon deixa de ser root e essa classe inteira de problema perde a mordida.</li>
<li><strong>Em CI, construa imagem sem daemon privilegiado.</strong> <a href="https://github.com/GoogleContainerTools/kaniko"target="_blank" rel="noopener">Kaniko</a> e <a href="https://buildah.io/"target="_blank" rel="noopener">Buildah</a> constroem imagens rootless, sem precisar montar socket nenhum no job.</li>
<li><strong>Não conseguiu rootless? Ligue o userns-remap.</strong> Custa uma tarde de testes com seus volumes e compra isolamento real entre o root do container e o root do host.</li>
<li><strong>No ai-jail, deixe o default trabalhar por você.</strong> Docker desligado, e <code>--docker</code> só nos projetos onde você confia no workload como confiaria num sudo. Regra prática: se você não daria <code>sudo</code> cego pro agente naquele diretório, também não dê <code>--docker</code>.</li>
</ol>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Uma sandbox bem construída perde boa parte do valor se tiver uma porta dos fundos aberta por conveniência. O bwrap, o Landlock, o seccomp e os rlimits do ai-jail continuam fazendo o trabalho deles, mas nenhum deles enxerga o que acontece quando o processo lá dentro pede gentilmente pro daemon root do host montar o filesystem inteiro num container. Camada de segurança que você não audita vira decoração.</p>
<p>Meu agradecimento público ao <a href="https://github.com/mdindoffer"target="_blank" rel="noopener">@mdindoffer</a>: report limpo, repro mínima, severidade correta e ainda por cima proposta de solução. É assim que se reporta vulnerabilidade em projeto open source.</p>
<p>Se você quer o contexto completo de como eu uso sandbox no dia a dia, escrevi sobre isso em <a href="/2026/07/11/como-me-precaver-pros-meus-agentes-nao-apagarem-minhas-coisas/">Como me precaver pros meus agentes não apagarem minhas coisas?</a>. A história do ai-jail está em <a href="/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/">AI Agents: Garantindo a Proteção do seu Sistema</a> e na <a href="/2026/03/01/ai-jail-sandbox-para-agentes-de-ia-de-shell-script-a-ferramenta-real/">reescrita em Rust</a>.</p>
<p>Agora vai lá e roda o upgrade.</p>
]]></content:encoded><category>ai-jail</category><category>containers</category><category>seguranca</category></item><item><title>Novidades no meu AI-MEMORY: cada vez melhor pra usar com suas IAs</title><link>https://akitaonrails.github.io/2026/07/20/novidades-no-meu-ai-memory-cada-vez-melhor-pra-usar-com-suas-ias/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/07/20/novidades-no-meu-ai-memory-cada-vez-melhor-pra-usar-com-suas-ias/</guid><pubDate>Mon, 20 Jul 2026 21:00:00 -0300</pubDate><description>&lt;p&gt;Faz pouco mais de um mês que publiquei &lt;a href="https://akitaonrails.github.io/2026/06/16/ai-memory-memoria-longo-prazo-karpathy-wiki-auto-aprendizado-hermes-projetos/"&gt;ai-memory: memória de longo prazo (Karpathy Wiki) e auto-aprendizado (Hermes) pros seus projetos&lt;/a&gt;. Foi dia 16 de junho. Naquele texto eu expliquei como sessões viravam páginas Markdown, como o auto-improve promovia aprendizados e por que memória ruim pode ser pior que amnésia.&lt;/p&gt;
&lt;p&gt;No mesmo dia saiu a versão 1.1.0. Hoje, 20 de julho, estamos na &lt;strong&gt;1.17.1&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Versão sozinha não quer dizer grande coisa, então fui contar o que aconteceu de verdade desde o horário daquele post. Foram &lt;strong&gt;31 releases, 55 pull requests mergeados e 46 issues fechadas&lt;/strong&gt;. Eu contei &lt;strong&gt;24 grupos de funcionalidades novas voltadas ao usuário&lt;/strong&gt; no &lt;a href="https://github.com/akitaonrails/ai-memory/blob/main/CHANGELOG.md"target="_blank" rel="noopener"&gt;CHANGELOG&lt;/a&gt;, ignorando correções, documentação e detalhes internos da mesma entrega. Se eu quebrasse a nova família &lt;code&gt;ai-memory run&lt;/code&gt; em auto-seleção, adoção de sessão, suporte a Crush, &lt;code&gt;--yolo&lt;/code&gt;, busca e recuperação, passava disso fácil.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Faz pouco mais de um mês que publiquei <a href="/2026/06/16/ai-memory-memoria-longo-prazo-karpathy-wiki-auto-aprendizado-hermes-projetos/">ai-memory: memória de longo prazo (Karpathy Wiki) e auto-aprendizado (Hermes) pros seus projetos</a>. Foi dia 16 de junho. Naquele texto eu expliquei como sessões viravam páginas Markdown, como o auto-improve promovia aprendizados e por que memória ruim pode ser pior que amnésia.</p>
<p>No mesmo dia saiu a versão 1.1.0. Hoje, 20 de julho, estamos na <strong>1.17.1</strong>.</p>
<p>Versão sozinha não quer dizer grande coisa, então fui contar o que aconteceu de verdade desde o horário daquele post. Foram <strong>31 releases, 55 pull requests mergeados e 46 issues fechadas</strong>. Eu contei <strong>24 grupos de funcionalidades novas voltadas ao usuário</strong> no <a href="https://github.com/akitaonrails/ai-memory/blob/main/CHANGELOG.md"target="_blank" rel="noopener">CHANGELOG</a>, ignorando correções, documentação e detalhes internos da mesma entrega. Se eu quebrasse a nova família <code>ai-memory run</code> em auto-seleção, adoção de sessão, suporte a Crush, <code>--yolo</code>, busca e recuperação, passava disso fácil.</p>
<p>Quinze pessoas tiveram PRs mergeados nesse período, quatorze além de mim. Preciso agradecer em especial ao <a href="https://github.com/djalmajr"target="_blank" rel="noopener">Djalma Júnior</a>, que sozinho teve 24 PRs mergeados, <a href="https://github.com/matheus-rodrigues00"target="_blank" rel="noopener">Matheus Rodrigues</a>, com cinco, <a href="https://github.com/lhzapata"target="_blank" rel="noopener">lhzapata</a>, com quatro, <a href="https://github.com/rthiago"target="_blank" rel="noopener">Thiago Silva</a> e <a href="https://github.com/cristianodewes"target="_blank" rel="noopener">Cristiano Dewes</a>, com dois cada. Mais nove contribuidores tiveram PR mergeado. Faz tempo que isso deixou de ser &ldquo;um programinha que eu fiz num fim de semana&rdquo;.</p>
<p>Não vou despejar 55 PRs aqui. Quero falar da mudança que mais afeta meu uso diário: o novo <strong><code>ai-memory run</code></strong>.</p>
<h2>A sessão também faz parte do projeto<span class="hx:absolute hx:-mt-20" id="a-sessão-também-faz-parte-do-projeto"></span>
    <a href="#a-sess%c3%a3o-tamb%c3%a9m-faz-parte-do-projeto" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Código é o resultado que sobreviveu. Ele não guarda a investigação inteira.</p>
<p>O Git mostra que você trocou Redis por SQLite. Talvez o commit explique uma parte. Mas onde ficou o experimento com Redis? Onde está o bug intermitente que só aparecia com dois workers? Quem lembrou que a primeira solução quebrava no Windows? Por que o time mudou de ideia depois de três horas? Qual workaround era temporário e qual virou decisão?</p>
<p>Boa parte disso só existe na sessão do agente.</p>
<p>Quando programo com Claude Code ou Codex, a sessão acumula o raciocínio operacional do projeto: perguntas, correções, tentativas descartadas, resultados de testes, surpresas, decisões e mudanças de direção. O código final mostra o que ficou. A sessão explica por que chegamos ali.</p>
<p>E sessão de LLM, no fim das contas, é texto. Claude Code grava JSONL. Codex também. OpenCode usa SQLite. Pi, OMP e Crush têm seus próprios formatos e diretórios. Cada harness empacota a conversa de um jeito, mas o conteúdo visível continua sendo mensagens e resultados de ferramentas.</p>
<p>Por que eu deveria deixar a parte mais cara do trabalho presa ao harness?</p>
<p>Anthropic pode mudar limite, preço, modelo ou formato amanhã. OpenAI também. Um agente pode estar excelente esta semana e insuportável na próxima. Eu quero trocar o motor sem jogar fora a viagem inteira.</p>
<p>Essa é a tese do ai-memory desde o começo: <strong>o programador tem que continuar independente das vontades dos providers</strong>. Modelo e harness são substituíveis. A memória do projeto é minha.</p>
<h2>O handoff resolvia só metade<span class="hx:absolute hx:-mt-20" id="o-handoff-resolvia-só-metade"></span>
    <a href="#o-handoff-resolvia-s%c3%b3-metade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O ai-memory já fazia passagem de bastão. Eu encerrava Claude Code, os hooks consolidavam a sessão e o Codex seguinte recebia um resumo curto com o que foi feito, perguntas abertas e próximos passos.</p>
<p>Isso continua existindo e continua útil. Quem abre <code>claude</code>, <code>codex</code> ou <code>opencode</code> diretamente mantém o comportamento anterior. O launcher novo é opt-in.</p>
<p>Mas handoff é uma compressão deliberadamente lossy. Ele preserva o que parece mais importante naquele momento. Não pretende transportar a sessão inteira, nem retomar a sessão nativa de cada harness. Se o resumo omitiu uma tentativa antiga que voltou a ser relevante duas horas depois, você precisa procurar na wiki ou nos registros.</p>
<p>O <code>ai-memory run</code> acrescenta outra camada: uma <strong>workstream gerenciada</strong>, uma linha de trabalho que atravessa os harnesses.</p>
<h2>Claude hoje, Codex daqui a pouco<span class="hx:absolute hx:-mt-20" id="claude-hoje-codex-daqui-a-pouco"></span>
    <a href="#claude-hoje-codex-daqui-a-pouco" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O uso básico é esse:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ~/Projects/meu-projeto
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ai-memory run claude
</span></span><span class="line"><span class="cl"><span class="c1"># trabalha, encerra o Claude Code normalmente</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ai-memory run codex --yolo
</span></span><span class="line"><span class="cl"><span class="c1"># Codex continua a mesma linha de trabalho</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ai-memory run claude --model opus
</span></span><span class="line"><span class="cl"><span class="c1"># volta pra sessão nativa anterior do Claude</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ai-memory run
</span></span><span class="line"><span class="cl"><span class="c1"># ou deixa o ai-memory escolher o harness certo pra continuar</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Não precisa de <code>--</code> pra separar os argumentos. Tudo depois do nome do harness segue pra ele, com uma exceção proposital: o <code>--yolo</code> pertence ao wrapper e é traduzido pra opção perigosa nativa de Claude Code, Codex, OpenCode, Pi ou Crush.</p>
<p>Por baixo, o ai-memory não tenta converter um arquivo do Codex num arquivo do Claude. Isso seria frágil e provavelmente quebraria na próxima atualização. Cada harness mantém sua própria sessão nativa. A workstream liga essas sessões a um ledger portátil com a parte visível da conversa.</p>
<p>Pensa assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sessão nativa do Claude ─┐
</span></span><span class="line"><span class="cl">sessão nativa do Codex  ─┼─ workstream ─ contexto portátil
</span></span><span class="line"><span class="cl">sessão do OpenCode      ─┤                 + busca completa
</span></span><span class="line"><span class="cl">sessão nativa do Pi     ─┘</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Na primeira vez que Claude entra naquela workstream, ele ganha uma sessão nativa. Quando você troca pra Codex, o Codex ganha a sessão nativa dele e recebe o delta portátil que ainda não viu. Ao voltar pro Claude, o launcher usa o <code>--resume</code> do próprio Claude Code e entrega apenas o que aconteceu nos outros harnesses desde a última vez.</p>
<p>Isso é bem diferente de começar um chat novo com um resumo colado no prompt. O Claude volta pra sessão real dele. O Codex volta pra sessão real dele. O ai-memory cuida da continuidade entre os dois.</p>
<p>Hoje o modo gerenciado suporta <strong>Claude Code, Codex, OpenCode, Pi, Crush e OMP</strong>. O suporte geral do ai-memory é maior, com MCP e hooks pra outros clientes, mas isso não quer dizer que todo cliente já tenha adapter de sessão nativa pro <code>run</code>. São contratos diferentes e eu prefiro deixar isso explícito.</p>
<h2>O que entra na workstream<span class="hx:absolute hx:-mt-20" id="o-que-entra-na-workstream"></span>
    <a href="#o-que-entra-na-workstream" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando o harness encerra, o ai-memory lê o final da sessão nativa sem modificar o store original. Entram mensagens visíveis de usuário e assistente, tool calls concluídas com seus resultados, resumos de compaction e um checkpoint não mutante do Git. Cada evento mantém a origem: veio do Claude, Codex, OpenCode, Pi, Crush ou OMP.</p>
<p>Credenciais do provider, registros criptografados, system/developer prompts e hidden reasoning ficam de fora. Formatos privados que o adapter não entende também ficam de fora e geram uma anotação de perda, em vez de o sistema fingir que importou tudo.</p>
<p>Os registros passam pelo sanitizer antes de entrar no ledger pesquisável e nos segmentos JSONL imutáveis do ai-memory. Os stores nativos são abertos read-only pelo adapter; quem continua escrevendo neles é o próprio harness.</p>
<p>Também não enfio a sessão inteira no próximo prompt. Isso recriaria o problema que o projeto tenta resolver: contexto bruto demais, caro demais e cheio de ruído. O próximo agente recebe um delta recente com tamanho limitado. Se uma decisão velha voltar a importar, o ledger completo continua pesquisável:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-memory workstream-search <span class="s2">&#34;por que desistimos do Redis&#34;</span>
</span></span><span class="line"><span class="cl">ai-memory workstream-search --limit <span class="m">50</span> --json <span class="s2">&#34;migration que falhou&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dentro de um <code>ai-memory run</code>, o ID da workstream já entra no ambiente. O agente não precisa descobrir UUID nenhum.</p>
<p>E o ledger não substitui a wiki. Ele resolve continuidade operacional. Decisões, regras, procedimentos e gotchas que precisam sobreviver por meses continuam merecendo páginas Markdown consolidadas. Sessão bruta é evidência. Wiki é conhecimento organizado.</p>
<h2>Posso manter mais de uma linha de trabalho?<span class="hx:absolute hx:-mt-20" id="posso-manter-mais-de-uma-linha-de-trabalho"></span>
    <a href="#posso-manter-mais-de-uma-linha-de-trabalho" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sim. A workstream padrão se chama <code>default</code>, selecionada por repositório e worktree. Se eu quiser abrir uma linha independente pra uma investigação sem contaminar o trabalho principal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-memory run --new investigar-race claude
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># depois continuo aquela linha com outro harness</span>
</span></span><span class="line"><span class="cl">ai-memory run --workstream investigar-race codex</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Uma lease impede duas janelas de escreverem ao mesmo tempo na mesma workstream. Em encerramento normal, o launcher importa o final da sessão e libera imediatamente. Se alguém matar o processo sem cleanup, a lease expira em até 90 segundos e a próxima execução retoma do último cursor confirmado, sem duplicar os eventos já importados.</p>
<p>Uma irritação besta que eu tinha o tempo todo: voltava pra um projeto depois de alguns dias e não lembrava se a sessão mais recente estava no Claude ou no Codex. Eu chutava um, abria, percebia que o contexto estava no outro, fechava e tentava de novo.</p>
<p>Agora só rodo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-memory run
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ou com a mesma seleção automática em YOLO mode</span>
</span></span><span class="line"><span class="cl">ai-memory run --yolo</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Numa workstream vazia, o ai-memory procura sessões locais daquele checkout em Claude Code, Codex, OpenCode, Pi e Crush, descobre qual é a mais recente e abre o harness certo pra mim. Numa workstream já estabelecida, a memória do servidor vence o timestamp de arquivo: ele volta ao último harness ligado àquele trabalho, em vez de adotar por engano uma sessão velha que só recebeu uma gravação mais recente. OMP continua disponível quando escolhido explicitamente, mas ainda não participa da seleção automática.</p>
<p>Na primeira execução explícita, o launcher também pode oferecer sessões existentes do mesmo checkout pra adoção. Dá pra começar a usar sem abandonar o chat que já estava aberto antes de atualizar o ai-memory.</p>
<h2>Outras novidades que eu realmente uso<span class="hx:absolute hx:-mt-20" id="outras-novidades-que-eu-realmente-uso"></span>
    <a href="#outras-novidades-que-eu-realmente-uso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu prometi não ler o changelog inteiro em voz alta, mas algumas mudanças desse mês merecem menção porque reduzem trabalho do programador.</p>
<h3>Briefing antes da primeira pergunta<span class="hx:absolute hx:-mt-20" id="briefing-antes-da-primeira-pergunta"></span>
    <a href="#briefing-antes-da-primeira-pergunta" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um projeto pode pedir um briefing automático no início de cada sessão:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">briefing</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">inject_on_session_start</span> <span class="p">=</span> <span class="s2">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">max_chars</span> <span class="p">=</span> <span class="mi">4000</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O ai-memory recompõe um pacote com páginas pinned, <code>_rules/</code>, <code>_slots/</code> e títulos recentes. O agente começa sabendo as regras e o estado básico do projeto, em vez de gastar a primeira conversa redescobrindo a arquitetura. É opt-in porque consome contexto em todo SessionStart, inclusive depois de um <code>/clear</code> do Claude.</p>
<h3>Preferências globais e busca entre projetos<span class="hx:absolute hx:-mt-20" id="preferências-globais-e-busca-entre-projetos"></span>
    <a href="#prefer%c3%aancias-globais-e-busca-entre-projetos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Preferências que valem pra todo meu trabalho agora podem viver no escopo reservado <code>_global</code>: estilo de código, ferramentas que prefiro, regras pessoais e convenções que não pertencem a um repositório específico. Consultas normais já fazem union desse escopo com o projeto atual.</p>
<p>Pra meta-repos que precisam consultar vários projetos irmãos o tempo todo, existe outro opt-in:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">recall</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">default_global</span> <span class="p">=</span> <span class="s2">&#34;true&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Assim <code>memory_query</code> e <code>memory_recent</code> sem escopo explícito procuram em todos os projetos. Não ligo isso em qualquer repo porque busca global à toa só traz ruído.</p>
<h3>Menos instrução fixa, mais Agent Skills<span class="hx:absolute hx:-mt-20" id="menos-instrução-fixa-mais-agent-skills"></span>
    <a href="#menos-instru%c3%a7%c3%a3o-fixa-mais-agent-skills" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O bloco de routing em <code>CLAUDE.md</code> ou <code>AGENTS.md</code> ficou pequeno. As instruções detalhadas de quando buscar memória, consolidar, guardar decisão ou criar handoff foram movidas pra Agent Skills gerenciadas. Um comando instala ou atualiza os dois sem atropelar o resto do arquivo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-memory install-instructions</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso também reduz drift. Quando o protocolo muda, atualizo o pacote gerenciado em vez de caçar um prompt velho copiado em quarenta repositórios.</p>
<h3>Privacidade antes da rede<span class="hx:absolute hx:-mt-20" id="privacidade-antes-da-rede"></span>
    <a href="#privacidade-antes-da-rede" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora cada repo pode excluir caminhos da captura:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">capture</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">ignore_paths</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;private/**&#34;</span><span class="p">,</span> <span class="s2">&#34;~/personal-notes/**&#34;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando uma integração reconhece uma file tool e o caminho bate, o evento é descartado localmente antes de spool, fila, rede ou servidor. Isso tem limites claros: não é DLP mágico, não interpreta qualquer shell command e não rastreia conteúdo citado em texto livre. Mas resolve o caso verificável sem vender uma garantia falsa.</p>
<p>Também entraram integrações com Kimi Code, Grok Build CLI, Devin e Zero, um provider OpenCode Zen/Go, <code>finalize-session</code> pro Codex e melhorias na administração de workspaces. Reforço: ter MCP e hook não significa automaticamente ter <code>ai-memory run</code>. O <a href="https://github.com/akitaonrails/ai-memory#support-matrix"target="_blank" rel="noopener">README</a> separa esses níveis.</p>
<h2>Bônus: ai-jail por fora, YOLO por dentro<span class="hx:absolute hx:-mt-20" id="bônus-ai-jail-por-fora-yolo-por-dentro"></span>
    <a href="#b%c3%b4nus-ai-jail-por-fora-yolo-por-dentro" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Há pouco mais de uma semana escrevi <a href="/2026/07/11/como-me-precaver-pros-meus-agentes-nao-apagarem-minhas-coisas/">como me protejo pra agentes não apagarem minhas coisas</a>. Minha recomendação continua a mesma: YOLO mode dá a melhor experiência, desde que o sistema limite o estrago possível.</p>
<p>O <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a> chegou à versão 1.15.0 e agora entende o launcher do ai-memory. O jeito que mais estou gostando de trabalhar virou:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail ai-memory run codex --yolo</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou, se quero que o ai-memory escolha qual harness deve continuar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail ai-memory run --yolo</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A ordem importa. <code>ai-jail</code> fica por fora para manter tanto o launcher quanto o processo filho na mesma sandbox. <code>ai-memory run</code> sozinho gerencia a sessão, mas não cria sandbox.</p>
<p>O <code>--yolo</code> desliga a cerimônia de aprovação do harness. O ai-jail faz a contenção: projeto atual read-write, home substituído por tmpfs, partes necessárias do estado dos agentes montadas seletivamente e diretórios sensíveis como <code>.ssh</code>, <code>.gnupg</code> e <code>.aws</code> fora da jaula. O suporte novo também deixa o estado local do ai-memory gravável pros hooks manterem spool e cursores entre execuções.</p>
<p>ai-jail reconhece tanto a camada <code>ai-memory</code> quanto o harness selecionado. Se você tem configuração global específica pra Codex, por exemplo, ela continua sendo aplicada quando o comando real é <code>ai-memory run codex</code>. Até o ajuste de redraw da status bar entende que o filho é Codex.</p>
<p>Essa combinação resolve as duas irritações ao mesmo tempo: o agente para de pedir confirmação a cada comando e a sessão para de pertencer ao agente. Fluidez sem dar o home inteiro de presente.</p>
<h2>One more thing: ai-usagebar também cresceu<span class="hx:absolute hx:-mt-20" id="one-more-thing-ai-usagebar-também-cresceu"></span>
    <a href="#one-more-thing-ai-usagebar-tamb%c3%a9m-cresceu" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando publiquei o <a href="/2026/05/24/criei-widget-waybar-omarchy-monitorar-uso-llms-ai-usagebar/">ai-usagebar original</a>, ele monitorava Claude, Codex, Z.AI e OpenRouter num widget da Waybar ou numa TUI. A versão 0.14.0 já cobre <strong>onze integrações de uso, gasto ou saldo</strong>.</p>
<p>Kimi ganhou as duas informações que realmente importam pra coding: quota semanal da assinatura e janela móvel de cinco horas. Kilo, Novita, Moonshot e Grok mostram saldo restante. Uma integração separada da Anthropic Console mostra gasto de API no mês, sem confundir isso com a assinatura do Claude Code. Ela deixa explícito que o Cost API não inclui Priority Tier.</p>
<p>Também dá pra cadastrar várias contas da Anthropic, ver limites semanais específicos de modelos como Fable e enxergar um marcador de ritmo. A barra não fica vermelha só porque chegou em 40%; ela compara o percentual gasto com quanto da janela já passou. Se usei 40% em 20% da semana, estou rápido demais. Essa é a informação útil.</p>
<p>O projeto deixou de ser só Waybar. Tem app nativo de menu bar no macOS, extensão do GNOME e a TUI standalone. E a novidade mais recente é um monitor local de contexto do Claude Code: aperto <code>c</code>, escolho uma das sessões recentes e vejo quanto da janela já foi consumido. Ele lê caudas limitadas dos JSONL locais e não inventa porcentagem quando não consegue determinar o tamanho da janela.</p>
<p>No meu fluxo, o trio acaba se encontrando naturalmente. <code>ai-usagebar</code> mostra qual assinatura ainda tem espaço. <code>ai-memory run</code> troca de harness sem jogar fora o trabalho. <code>ai-jail</code> deixa o escolhido trabalhar em YOLO sem acesso irrestrito à máquina.</p>
<h2>Atualizando<span class="hx:absolute hx:-mt-20" id="atualizando"></span>
    <a href="#atualizando" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>As páginas de instalação dos três projetos têm todos os métodos, incluindo AUR, Homebrew e binários de release:</p>
<ul>
<li><a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a></li>
<li><a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a></li>
<li><a href="https://github.com/akitaonrails/ai-usagebar"target="_blank" rel="noopener">ai-usagebar</a></li>
</ul>
<p>Depois de atualizar o ai-memory, reinstale os hooks dos harnesses que pretende usar no modo gerenciado. Os três principais no meu caso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-memory install-hooks --agent claude-code --apply
</span></span><span class="line"><span class="cl">ai-memory install-hooks --agent codex --apply
</span></span><span class="line"><span class="cl">ai-memory install-hooks --agent opencode --apply</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Crush não precisa de hook no modo gerenciado; ele recebe o contexto por um arquivo temporário configurado só pro processo lançado. A <a href="https://github.com/akitaonrails/ai-memory/blob/main/docs/managed-workstreams.md"target="_blank" rel="noopener">documentação de managed workstreams</a> tem os detalhes de instalação, privacidade, recuperação e os argumentos nativos de cada adapter.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Um mês atrás, o ai-memory já conseguia transformar sessão em wiki e entregar um handoff pro próximo agente. Agora ele consegue gerenciar a própria linha de trabalho: adota uma sessão existente, mantém uma sessão nativa por harness, transporta o histórico visível, retoma cada cliente no formato dele e deixa o ledger inteiro pesquisável.</p>
<p>Pra mim, é aí que está o valor. O código contém a implementação que venceu. A sessão contém as decisões, experimentos que falharam, gotchas, bugs inesperados e mudanças de ideia que produziram aquela implementação. Jogar isso fora toda vez que muda de provider é desperdício.</p>
<p>Raw transcript também não resolve sozinho. É grande, repetitivo e caro pra enfiar em qualquer contexto. O ai-memory mantém a evidência, entrega só o delta recente e usa consolidação pra transformar o que merece sobreviver em páginas Markdown curtas.</p>
<p>Ainda há formatos novos pra suportar, arestas de plataforma e muito teste pela frente. A diferença é que agora existe uma base real, com acceptance test chamando harness de verdade, 14 contribuidores externos com PR mergeado em pouco mais de um mês e uso diário empurrando o desenho.</p>
<p>LLM e assinatura eu alugo de quem estiver entregando melhor hoje. A sessão do meu projeto fica comigo.</p>
]]></content:encoded><category>ai-memory</category><category>agentes-de-codigo</category><category>ai-jail</category><category>ai-usagebar</category></item><item><title>Como me precaver pros meus agentes não apagarem minhas coisas?</title><link>https://akitaonrails.github.io/2026/07/11/como-me-precaver-pros-meus-agentes-nao-apagarem-minhas-coisas/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/07/11/como-me-precaver-pros-meus-agentes-nao-apagarem-minhas-coisas/</guid><pubDate>Sat, 11 Jul 2026 21:00:00 -0300</pubDate><description>&lt;p&gt;Essa semana o assunto no Twitter foi LLM apagando arquivo dos outros. O &lt;a href="https://x.com/mattshumer_/status/2075657271401390161"target="_blank" rel="noopener"&gt;Matt Shumer postou&lt;/a&gt; que o GPT-5.6-Sol acidentalmente deletou quase TODOS os arquivos do Mac dele. Um subagente de review expandiu errado a variável &lt;code&gt;$HOME&lt;/code&gt; e rodou &lt;code&gt;rm -rf /Users/mattsdevbox&lt;/code&gt;. Ele matou o processo no meio, mas o estrago já estava feito.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/11/agentes-apagando/tweet-mattshumer.webp" alt="Tweet do Matt Shumer: GPT-5.6-Sol acidentalmente deletou quase todos os arquivos do Mac dele, rodando rm -rf /Users/mattsdevbox por causa de uma expansão errada de $HOME." loading="lazy" /&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>Essa semana o assunto no Twitter foi LLM apagando arquivo dos outros. O <a href="https://x.com/mattshumer_/status/2075657271401390161"target="_blank" rel="noopener">Matt Shumer postou</a> que o GPT-5.6-Sol acidentalmente deletou quase TODOS os arquivos do Mac dele. Um subagente de review expandiu errado a variável <code>$HOME</code> e rodou <code>rm -rf /Users/mattsdevbox</code>. Ele matou o processo no meio, mas o estrago já estava feito.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/11/agentes-apagando/tweet-mattshumer.webp" alt="Tweet do Matt Shumer: GPT-5.6-Sol acidentalmente deletou quase todos os arquivos do Mac dele, rodando rm -rf /Users/mattsdevbox por causa de uma expansão errada de $HOME."  loading="lazy" /></p>
<p>No dia seguinte o <a href="https://x.com/goodalexander/status/2076019436767584347"target="_blank" rel="noopener">goodalexander reproduziu</a> o mesmo comportamento num devnet descartável: um <code>rsync --delete-delay</code> contra <code>/</code> varreu <code>/var</code>, <code>/etc</code> e o estado dos validators dele, corrompendo o filesystem a ponto de precisar reinstalar o sistema operacional. A conclusão dele: &ldquo;you can&rsquo;t use this thing in prod&rdquo;.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/11/agentes-apagando/tweet-goodalexander.webp" alt="Tweet do goodalexander: reproduziu o comportamento destrutivo do GPT 5.6 Sol num devnet descartável, com rsync –delete-delay apagando /var e /etc e corrompendo o filesystem."  loading="lazy" /></p>
<p>Casos assim aparecem de tempos em tempos desde que agentes de coding viraram mainstream. E toda vez a discussão descamba pros mesmos dois extremos: de um lado &ldquo;viu? IA é perigosa, nunca deixe rodar nada sozinha&rdquo;, do outro &ldquo;quem deixa um agente solto no sistema merece o que acontece&rdquo;. Eu acho os dois lados preguiçosos. Deixa eu contar minha experiência real e o que eu faço a respeito.</p>
<h2>Cinco meses de maratona, zero acidentes<span class="hx:absolute hx:-mt-20" id="cinco-meses-de-maratona-zero-acidentes"></span>
    <a href="#cinco-meses-de-maratona-zero-acidentes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Desde fevereiro eu estou maratonando Claude Code, Codex e OpenCode. Quase todos os dias, quase o tempo todo, em sessões muito longas que às vezes atravessam dias. Estourando o limite do meu plano Max da Anthropic e do meu plano Plus da OpenAI com folga. O resultado concreto até agora: quase 40 repositórios novos no meu GitHub e mais de 500 mil linhas de código produzidas.</p>
<p>E nos últimos dois meses eu rodo tudo exclusivamente em &ldquo;YOLO mode&rdquo;. Pra quem não conhece o jargão: por padrão, esses agentes pedem permissão antes de cada comando que vão executar no seu terminal. O YOLO mode desliga essa trava. No Claude Code é a flag <code>--dangerously-skip-permissions</code>, no Codex é o <code>--yolo</code>, no OpenCode você configura as permissões pra aprovar tudo. O agente roda o que quiser, quando quiser, sem me perguntar nada.</p>
<p>Em cinco meses nesse regime, nem uma vez um agente apagou um arquivo que eu não pedi. Nem uma vez rodou um comando destrutivo fora do escopo. As frustrações que eu tenho com agentes são de outra natureza (teimosia, solução preguiçosa, contexto que se perde), coisas irritantes mas a quilômetros de distância de um <code>rm -rf</code> no meu home.</p>
<p>Então quando eu vejo esses relatos, minha primeira hipótese sempre vai pro mesmo lugar: o prompt. Pra um agente &ldquo;enlouquecer&rdquo; desse jeito, em geral o usuário escreveu instruções muito ruins. Vagas, ambíguas, pedindo limpeza sem definir o que é lixo, mandando o agente &ldquo;resolver&rdquo; sem dar contexto. Falta de habilidade de comunicação combinada com falta de conhecimento técnico pra perceber que o pedido estava mal formulado. O agente executa a instrução que você escreveu, com todas as ambiguidades que você deixou nela.</p>
<p>Dito isso, eu NÃO confio completamente em LLMs. Ninguém deveria.</p>
<blockquote>
  <p>A diferença é que a minha desconfiança vira engenharia em vez de virar tweet.</p>

</blockquote>
<p>Camada por camada, é assim que eu durmo tranquilo.</p>
<h2>Camada 1: sandbox com ai-jail<span class="hx:absolute hx:-mt-20" id="camada-1-sandbox-com-ai-jail"></span>
    <a href="#camada-1-sandbox-com-ai-jail" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando eu realmente suspeito que uma sessão pode fazer besteira (vou mexer em scripts de sistema, vou deixar o agente rodar por horas sem supervisão num código que toca no filesystem), eu abro o harness dentro do <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a>, uma ferramenta minha, open source, escrita em Rust.</p>
<p>O ai-jail é um wrapper de sandbox pra agentes de coding. No Linux ele usa o <code>bwrap</code> (bubblewrap, a mesma base do Flatpak), no macOS usa o <code>sandbox-exec</code>. A ideia: o processo enxerga um filesystem que parece normal, mas só o diretório do projeto atual é real e persistente. O resto do <code>$HOME</code>, os diretórios vizinhos, o <code>/tmp</code>, tudo vive em tmpfs e evapora quando a sessão termina. O estado do agente (<code>~/.claude</code>, <code>~/.codex</code> etc) é montado de verdade pra ele continuar logado e configurado, mas <code>~/.ssh</code>, <code>~/.gnupg</code>, <code>~/.aws</code> e perfis de browser nunca entram na jaula.</p>
<p>Instalar é um comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Arch Linux</span>
</span></span><span class="line"><span class="cl">yay -S ai-jail-bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># macOS / Linux via Homebrew</span>
</span></span><span class="line"><span class="cl">brew tap akitaonrails/tap <span class="o">&amp;&amp;</span> brew install ai-jail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ou direto do crates.io</span>
</span></span><span class="line"><span class="cl">cargo install ai-jail</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E usar é mais simples ainda:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ~/Projects/meu-app
</span></span><span class="line"><span class="cl">ai-jail claude       <span class="c1"># Claude Code na jaula</span>
</span></span><span class="line"><span class="cl">ai-jail codex        <span class="c1"># Codex na jaula</span>
</span></span><span class="line"><span class="cl">ai-jail --dry-run claude  <span class="c1"># mostra a política sem rodar</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se o agente resolver rodar <code>rm -rf ~</code> lá dentro, ele apaga um tmpfs que ia sumir de qualquer jeito. Meu home real nem foi montado. Pra quem está começando com YOLO mode e ainda não tem calo, é o jeito mais barato de errar sem consequência.</p>
<h2>Camada 2: BTRFS e snapshots (a rede de segurança de verdade)<span class="hx:absolute hx:-mt-20" id="camada-2-btrfs-e-snapshots-a-rede-de-segurança-de-verdade"></span>
    <a href="#camada-2-btrfs-e-snapshots-a-rede-de-seguran%c3%a7a-de-verdade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora o ponto principal desse post. Eu advogo há ANOS que usuário de Linux deveria usar BTRFS em vez de EXT4. Fiz <a href="/2023/10/19/akitando-146-protegendo-e-recuperando-dados-perdidos-git-backup-btrfs/">um episódio inteiro do Akitando sobre isso em 2023</a>, muito antes de existir agente de coding pra levar a culpa. O argumento era o mesmo: seus dados precisam sobreviver a erro humano. Um LLM rodando <code>rm -rf</code> no lugar errado é só uma categoria nova de erro humano, terceirizado.</p>
<p>Por que BTRFS é superior pra desktop? Copy-on-write (CoW): o filesystem nunca sobrescreve um bloco no lugar, ele escreve a versão nova em outro bloco e atualiza os ponteiros. Checksum de dados e metadados, então bitrot silencioso é detectado em vez de corromper seu arquivo caladinho. Compressão transparente. Subvolumes, que funcionam como partições flexíveis sem tamanho fixo. E a consequência mais valiosa do CoW: <strong>snapshots leves</strong>.</p>
<p>Um snapshot BTRFS é instantâneo e custa perto de zero espaço no momento em que é criado. Ele congela os ponteiros dos blocos atuais em vez de copiar dados. Só o que divergir a partir dali (blocos novos ou modificados) passa a ocupar espaço extra. Na prática você pode tirar dezenas de snapshots por dia sem sentir. Neste exato momento minha máquina mantém 23 snapshots do sistema, e o custo é a diferença entre eles, alguns poucos gigas.</p>
<h3>Agendando snapshots<span class="hx:absolute hx:-mt-20" id="agendando-snapshots"></span>
    <a href="#agendando-snapshots" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As duas ferramentas clássicas são o <strong>Timeshift</strong> e o <strong>snapper</strong>. O Timeshift é o mais amigável: interface gráfica, você marca &ldquo;quantos snapshots manter&rdquo; por hora/dia/semana/mês e ele cuida do resto. Ele espera o layout de subvolumes <code>@</code> (raiz) e <code>@home</code>, que é o padrão da maioria das distros que instalam em BTRFS hoje. O snapper (nascido no openSUSE) é mais granular: configs por subvolume, timeline automática, integração com o gerenciador de pacotes pra tirar snapshot antes e depois de cada instalação.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Timeshift: snapshot manual antes de uma sessão suspeita</span>
</span></span><span class="line"><span class="cl">sudo timeshift --create --comments <span class="s2">&#34;antes de soltar o agente&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># snapper: criar config pro /home e ligar a timeline</span>
</span></span><span class="line"><span class="cl">sudo snapper -c home create-config /home
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now snapper-timeline.timer</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Meu setup roda Timeshift agendado, mais o <code>grub-btrfsd</code> de plantão (já explico por quê).</p>
<h3>Recuperando arquivos apagados<span class="hx:absolute hx:-mt-20" id="recuperando-arquivos-apagados"></span>
    <a href="#recuperando-arquivos-apagados" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui está a mágica que todo mundo que perdeu arquivo pra um agente gostaria de ter tido. Snapshot BTRFS é navegável como um diretório comum. O agente apagou seu projeto às 15h? O snapshot das 14h ainda tem tudo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># com Timeshift, os snapshots ficam montados em /run/timeshift</span>
</span></span><span class="line"><span class="cl">sudo ls /run/timeshift/backup/timeshift-btrfs/snapshots/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># achou o snapshot de antes do estrago? copia de volta e pronto</span>
</span></span><span class="line"><span class="cl">sudo cp -a /run/timeshift/backup/timeshift-btrfs/snapshots/2026-07-11_14-00-01/@home/akitaonrails/Projects/meu-app ~/Projects/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com snapper é a mesma coisa via <code>.snapshots</code> dentro do subvolume, e ele ainda tem o <code>snapper undochange</code> pra reverter mudanças específicas entre dois snapshots. Recuperação em segundos, sem drama, sem ferramenta forense.</p>
<h3>E se o sistema nem ligar mais?<span class="hx:absolute hx:-mt-20" id="e-se-o-sistema-nem-ligar-mais"></span>
    <a href="#e-se-o-sistema-nem-ligar-mais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O caso do goodalexander foi além de perder arquivos: o agente varreu <code>/var</code> e <code>/etc</code> e o sistema ficou imprestável, precisou reinstalar o OS. Com BTRFS nem isso seria necessário. É pra esse cenário que existe o <a href="https://github.com/Antynea/grub-btrfs"target="_blank" rel="noopener">grub-btrfs</a>: um daemon que observa seus snapshots e regenera o menu do GRUB com uma entrada de boot pra cada um.</p>
<p>Sistema não sobe mais? Reinicia, segura o menu do GRUB, escolhe &ldquo;snapshots&rdquo; e dá boot direto num snapshot de ontem, read-only. De dentro dele, roda <code>timeshift --restore</code>, escolhe o snapshot bom, reinicia de novo. Sistema inteiro de volta ao estado de antes do desastre, em menos tempo do que você levaria pra achar o pendrive de instalação. No pior caso absoluto (o próprio GRUB foi junto), um live USB qualquer monta a partição BTRFS, aponta o subvolume padrão de volta pro snapshot bom e resolve.</p>
<h3>E no macOS? E no Windows?<span class="hx:absolute hx:-mt-20" id="e-no-macos-e-no-windows"></span>
    <a href="#e-no-macos-e-no-windows" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Usuário de Mac tem o equivalente direto: o <strong>APFS</strong>, filesystem padrão desde o High Sierra (2017), também é copy-on-write e também tem snapshots leves. Quem gerencia é o Time Machine: com ele ativado, o macOS tira snapshots locais automáticos de hora em hora e guarda as últimas 24 horas no próprio disco, independente do backup externo. E dá pra criar um na mão antes de soltar o agente:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># criar um snapshot local do APFS agora</span>
</span></span><span class="line"><span class="cl">tmutil localsnapshot
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># listar os snapshots existentes</span>
</span></span><span class="line"><span class="cl">tmutil listlocalsnapshots /</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra recuperar, abre o próprio Time Machine (&ldquo;Browse Time Machine Backups&rdquo; na barra de menu): ele navega pelos snapshots locais mesmo sem nenhum disco externo plugado, e você restaura o arquivo dali. Quem prefere terminal monta o snapshot read-only com <code>mount_apfs -s</code> e copia de volta. Ou seja, o caso do Matt Shumer lá do começo tinha solução nativa no próprio Mac dele: um snapshot de uma hora antes teria devolvido quase tudo.</p>
<p>No Windows a história é mais fraca. O NTFS tem o Volume Shadow Copy (VSS), que alimenta os restore points e a aba &ldquo;Versões Anteriores&rdquo;, mas é voltado pra arquivos de sistema e raramente está cobrindo seus dados na hora que você precisa. Nada com a leveza e a previsibilidade de um snapshot CoW agendado. Se você desenvolve no Windows, trate backup externo como obrigatório, sem discussão (e o restic da próxima seção roda em Windows numa boa).</p>
<h2>Camada 3: backup externo com restic<span class="hx:absolute hx:-mt-20" id="camada-3-backup-externo-com-restic"></span>
    <a href="#camada-3-backup-externo-com-restic" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Snapshot mora no mesmo disco que os dados. Se o NVMe morrer, morrem os dois juntos. Por isso a outra coisa que eu repito há anos: <strong>todo mundo precisa de backup externo</strong>, seja num HD externo, seja num NAS. Eu uso um Synology na rede local, montado via NFS, e <a href="/2025/04/17/configurando-meu-nas-synology-com-nfs-no-linux/">documentei o setup completo aqui</a>.</p>
<p>Em cima dele roda o <a href="https://restic.net/"target="_blank" rel="noopener">restic</a>, que na minha opinião é a melhor ferramenta de backup pra essa finalidade: deduplicação, criptografia, snapshots incrementais e restore simples. Minha configuração real é um script disparado por um timer do systemd todo dia às 3 da manhã. Ele confere se o NAS está montado, faz o backup do <code>/home</code> e do <code>/mnt/data/Projects</code> pulando caches (<code>node_modules</code>, <code>target/</code> do Rust, <code>__pycache__</code> e afins, tudo num arquivo de excludes), e depois aplica a política de retenção: 7 diários, 4 semanais, 6 mensais, 1 anual.</p>
<p>Reproduzir o essencial é isso aqui:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># uma vez: criar o repositório no NAS</span>
</span></span><span class="line"><span class="cl">restic init --repo /mnt/nas/desktop-backup
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># todo dia (via cron ou systemd timer):</span>
</span></span><span class="line"><span class="cl">restic -r /mnt/nas/desktop-backup backup ~ /mnt/data/Projects <span class="se">\
</span></span></span><span class="line"><span class="cl">    --exclude-file<span class="o">=</span>~/.config/restic/excludes --exclude-caches
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">restic -r /mnt/nas/desktop-backup forget <span class="se">\
</span></span></span><span class="line"><span class="cl">    --keep-daily <span class="m">7</span> --keep-weekly <span class="m">4</span> --keep-monthly <span class="m">6</span> --keep-yearly <span class="m">1</span> --prune</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E se o dia ruim chegar de verdade (agente destrutivo, disco morto, ransomware, tanto faz a causa):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># listar os snapshots disponíveis</span>
</span></span><span class="line"><span class="cl">restic -r /mnt/nas/desktop-backup snapshots
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># restaurar um arquivo ou diretório específico</span>
</span></span><span class="line"><span class="cl">restic -r /mnt/nas/desktop-backup restore latest <span class="se">\
</span></span></span><span class="line"><span class="cl">    --target /tmp/recuperado --include /home/akitaonrails/Projects/meu-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ou montar o repositório inteiro como filesystem e navegar com calma</span>
</span></span><span class="line"><span class="cl">restic -r /mnt/nas/desktop-backup mount /mnt/restic</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Repara na arquitetura em camadas: o snapshot BTRFS resolve o acidente de minutos atrás em segundos. O restic resolve o desastre de dias atrás, incluindo hardware morto. Um agente de IA teria que ser muito dedicado pra atravessar as duas camadas, e mesmo assim ainda existiria a cópia de ontem no NAS, que ele nem alcança.</p>
<h2>E nas empresas? Profissionalismo<span class="hx:absolute hx:-mt-20" id="e-nas-empresas-profissionalismo"></span>
    <a href="#e-nas-empresas-profissionalismo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tudo acima é sobre a minha máquina pessoal. Agora sobe o sarrafo: ambiente corporativo.</p>
<p>A regra número um é simples e inegociável: <strong>nenhum agente de IA deve ter acesso a produção. Nunca.</strong> Nem SSH direto em servidor de produção, nem credencial de banco de produção, nem token de admin de serviço em produção. Se o seu agente consegue rodar um <code>DELETE</code> na base que atende clientes, o problema deixou de ser a IA faz tempo: qualquer estagiário com o mesmo acesso é a mesma bomba-relógio, só com menos velocidade de digitação.</p>
<p>Agente em <strong>staging</strong>, por outro lado, eu defendo. É pra isso que staging existe. Mas mesmo em staging tem jeito certo: você resiste à tentação de pedir ações one-shot (&ldquo;roda um <code>apt install</code> aí&rdquo;, &ldquo;dá um <code>systemctl restart</code>&rdquo;, &ldquo;conecta no postgres e executa isso&rdquo;). Comando avulso executado na mão é conhecimento que evapora. Sua infraestrutura precisa ser automatizada e reproduzível: Ansible, Kubernetes, Terraform, o que fizer sentido pro seu stack. O papel do agente passa a ser escrever e refinar essas receitas, que ficam versionadas em git.</p>
<h3>Um exemplo pequeno, aqui da minha mesa<span class="hx:absolute hx:-mt-20" id="um-exemplo-pequeno-aqui-da-minha-mesa"></span>
    <a href="#um-exemplo-pequeno-aqui-da-minha-mesa" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Eu tenho um ambiente de jogos no meu PC principal, mas eu me recuso a misturar pacote de emulador com meu ambiente de desenvolvimento. Então isolei os jogos dentro de um <a href="https://github.com/89luca89/distrobox"target="_blank" rel="noopener">distrobox</a>. Pra quem não conhece: o distrobox cria containers (via podman ou docker) que se integram com seu desktop como se fossem nativos. Compartilham seu home (ou um home dedicado), acesso a GPU, Wayland, áudio. Na prática é uma distro completa dentro da sua distro, com espaço de pacotes separado.</p>
<p>O <a href="https://github.com/akitaonrails/distrobox-gaming"target="_blank" rel="noopener">distrobox-gaming</a> é um repositório de playbooks Ansible que constrói uma box Arch chamada <code>gaming</code> do zero: ES-DE de frontend, emuladores standalone (shadPS4, RPCS3, PCSX2, Dolphin, DuckStation, Eden e mais uma dúzia), cores do RetroArch, configs otimizadas por jogo, atalhos no launcher do host. E o <a href="https://github.com/akitaonrails/distrobox-llm"target="_blank" rel="noopener">distrobox-llm</a> faz o mesmo pra uma box de trabalho com LLMs locais: CUDA, cuDNN, Ollama, whisper.cpp e LM Studio isolados numa box com <code>--nvidia</code>, enquanto o host só carrega o driver. Upgrade de versão maior do CUDA deixa de chacoalhar meu ambiente de desenvolvimento.</p>
<p>Quem escreveu a esmagadora maioria desses playbooks? Os agentes. Em vez de pedir &ldquo;instala o RPCS3 pra mim&rdquo;, meu prompt é &ldquo;escreve a receita Ansible idempotente que instala e configura o RPCS3, roda ela, verifica o resultado e corrige até passar&rdquo;. Olha um trecho real do role que cria a box do LLM:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check if distrobox exists</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ansible.builtin.shell</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;distrobox list 2&gt;/dev/null | awk &#39;{print $3}&#39; | grep -qx {{ dl_box_name }}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">register</span><span class="p">:</span><span class="w"> </span><span class="l">_box_exists</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">changed_when</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">failed_when</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create distrobox</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ansible.builtin.command</span><span class="p">:</span><span class="w"> </span><span class="p">&gt;-</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    distrobox create
</span></span></span><span class="line"><span class="cl"><span class="sd">    --name {{ dl_box_name }}
</span></span></span><span class="line"><span class="cl"><span class="sd">    --image {{ dl_image }}
</span></span></span><span class="line"><span class="cl"><span class="sd">    --home {{ dl_box_home }}
</span></span></span><span class="line"><span class="cl"><span class="sd">    {{ &#39;--nvidia&#39; if dl_nvidia_enabled else &#39;&#39; }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l">_box_exists.rc != 0</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Idempotente: confere se a box existe antes de criar, rodar duas vezes dá no mesmo. Se meu PC pegar fogo amanhã, eu recrio os dois ambientes inteiros com um <code>ansible-playbook site.yml</code> em cada repo. O conhecimento está na receita, versionado, auditável. Zero comando perdido no histórico do shell.</p>
<h3>Escalando o mesmo princípio pra produção<span class="hx:absolute hx:-mt-20" id="escalando-o-mesmo-princípio-pra-produção"></span>
    <a href="#escalando-o-mesmo-princ%c3%adpio-pra-produ%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Numa empresa, troca distrobox por Kubernetes e o princípio é idêntico. O agente escreve o manifesto, testa no cluster de staging, você promove pra produção manualmente:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">minha-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">minha-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">minha-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">minha-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">registry.exemplo.com/minha-api:v42</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">readinessProbe</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">httpGet</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/healthz</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">limits</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">512Mi</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O fluxo profissional: o agente produz e ajusta o manifesto (ou o chart do Helm, ou o módulo do Terraform), aplica em staging (<code>kubectl apply -n staging</code>), roda os testes, observa efeitos colaterais, bugs, comportamento não intencional, e itera até você estar satisfeito. Aí VOCÊ, humano, promove pra produção (via PR revisado num fluxo GitOps, ou um <code>kubectl apply</code> manual e consciente), monitorando cada passo. O agente fez todo o trabalho pesado: os artefatos, os procedimentos, os checklists. Dá até pra pedir pra ele gerar a documentação de auditoria no final. Mas o dedo que aperta o botão de produção tem CPF.</p>
<h3>Até o SQL &ldquo;simples&rdquo; de manutenção<span class="hx:absolute hx:-mt-20" id="até-o-sql-simples-de-manutenção"></span>
    <a href="#at%c3%a9-o-sql-simples-de-manuten%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Um caso que parece inofensivo e me incomoda muito: comando SQL de manutenção em produção. Muita gente sempre rodou esses comandos na mão, direto no client de produção, e agora está pedindo pro agente escrever E executar. Isso é inaceitável, com ou sem IA. Seu sistema deve ter um fluxo de <strong>migrations</strong>: a mudança vira código versionado, roda em staging primeiro, é validada, e só então executa em produção dentro do processo normal de deploy.</p>
<p>E &ldquo;produção&rdquo;, por definição, significa duas coisas que muita empresa finge ter. Primeiro: backups de verdade, testados, com restore ensaiado.</p>
<blockquote>
  <p>Jamais rode em produção qualquer coisa da qual você não consegue se recuperar.</p>

</blockquote>
<p>Segundo: monitoramento. Se você não tem visibilidade do estado do sistema, você não tem produção, tem um servidor ligado na tomada e reza.</p>
<h2>Monitoramento: até no meu humilde PC<span class="hx:absolute hx:-mt-20" id="monitoramento-até-no-meu-humilde-pc"></span>
    <a href="#monitoramento-at%c3%a9-no-meu-humilde-pc" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E monitoramento é tão parte da disciplina que eu aplico até na minha máquina pessoal. Recentemente eu desenvolvi um widget de system health pro meu <a href="https://github.com/akitaonrails/clock-tui"target="_blank" rel="noopener">clock-tui</a> (meu fork mantido do clock-tui original, com widgets de comando no modo relógio). Ele fica aberto num canto mostrando isso aqui:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/07/11/agentes-apagando/system-health-widget.webp" alt="Widget de system health do clock-tui: backups restic ok há 19h, 23 snapshots do timeshift, limpeza de dev-cache ok, scrub e trim do btrfs em dia, zero erros de IO, load, memória e uso de storage por filesystem."  loading="lazy" /></p>
<p>Uma linha de veredito (&ldquo;all systems healthy&rdquo;) e o estado de tudo que este post descreveu: o backup do restic rodou há 19 horas e o próximo é às 03:03; o Timeshift mantém 23 snapshots; a limpeza de caches de desenvolvimento rodou há 32 horas; o scrub do BTRFS está em dia em cada filesystem, com trim recente, alocação saudável e zero erros de I/O; mais zumbis, load, memória e ocupação de cada disco. O widget (<code>tclock-system-health</code>) vem junto no repositório e no pacote do AUR, e detecta a maior parte disso sozinho.</p>
<p>Por trás dele estão as manutenções automatizadas, todas em timers do systemd: o restic diário às 3h; o <code>dev-cache-clean</code> (dia 1 e dia 15) varrendo <code>target/</code> órfãos de Rust e cache do yay, que juntos devolvem dezenas de gigas; o <code>btrfs-scrub</code> mensal por filesystem validando checksums; o <code>fstrim</code> semanal. Nada disso exige que eu lembre de nada. Quando algum atrasar ou falhar, o widget muda de cor na minha cara.</p>
<p>Esse é o nível caseiro, proporcional a um PC pessoal. Numa empresa você vai de full-blown: Prometheus coletando métricas, Grafana desenhando os dashboards, Alertmanager acordando o plantonista às 3 da manhã. A escala muda, o princípio é o mesmo: o estado do sistema visível o tempo todo, e alguém (ou alguma coisa) sendo avisado quando sai do trilho.</p>
<h2>Conclusão: disciplina<span class="hx:absolute hx:-mt-20" id="conclusão-disciplina"></span>
    <a href="#conclus%c3%a3o-disciplina" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Volta nos dois tweets do começo. Em ambos os casos, um único comando errado causou perda real, e a discussão pública virou &ldquo;em quem eu confio mais&rdquo;. A pergunta certa é outra: <strong>por que um único comando errado, de qualquer origem, tem o poder de causar perda irrecuperável na sua máquina?</strong></p>
<p>Ser profissional é ser disciplinado, e a disciplina começa em casa. Meu PC pessoal opera com o mesmo rigor que eu cobraria de um ambiente corporativo: filesystem com snapshots leves e agendados, boot recuperável via GRUB, backup externo diário com política de retenção, sandbox pra sessões suspeitas, ambientes isolados e reconstruíveis por receitas versionadas, manutenção automatizada e um monitor que me avisa quando qualquer peça atrasa. Custo de manutenção mensal disso tudo: perto de zero. Foi montado uma vez (em boa parte pelos próprios agentes) e funciona sozinho.</p>
<p>Com essas camadas, a pergunta do título quase perde o sentido. O agente apagou um diretório? Snapshot de uma hora atrás, dois minutos de recuperação. Destruiu o sistema? Boot num snapshot pelo GRUB, restore, dez minutos. Fritou o disco junto? Restic no NAS, backup de ontem. Em cinco meses de YOLO mode eu nunca precisei de nenhuma dessas redes pra acidente de IA. Mas eu durmo tranquilo justamente porque elas estão lá.</p>
<p>LLM rodando solto no seu sistema é só o teste de estresse mais recente da sua disciplina. Se um agente descontrolado consegue causar perda permanente nos seus dados, a vulnerabilidade sempre esteve aí, esperando um erro seu, um disco ruim ou um ransomware. O agente foi só o mensageiro que chegou primeiro.</p>
]]></content:encoded><category>ai-jail</category><category>agentes-de-codigo</category><category>seguranca</category><category>armazenamento-e-backup</category></item><item><title>Dicas e Toolkit de IA do Akita: ai-jail, ai-memory, ai-usagebar</title><link>https://akitaonrails.github.io/2026/05/24/dicas-e-toolkit-de-ia-do-akita-ai-jail-ai-memory-ai-usagebar/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/05/24/dicas-e-toolkit-de-ia-do-akita-ai-jail-ai-memory-ai-usagebar/</guid><pubDate>Sun, 24 May 2026 10:30:00 -0300</pubDate><description>&lt;p&gt;No post da semana passada, &lt;a href="https://akitaonrails.github.io/2026/05/14/terminando-maratona-ia-sucesso-ou-fracasso/"&gt;&amp;ldquo;Terminando a Maratona de IA: Sucesso ou Fracasso?&amp;rdquo;&lt;/a&gt;, eu encerrei a primeira fase da minha maratona. Mais de 600 horas de uso intenso de agentes de código, dezenas de projetos, centenas de milhares de linhas de código e documentação, e uma pilha de conclusões práticas. Não de marketing. Práticas.&lt;/p&gt;
&lt;p&gt;Vou repetir uma coisa importante antes que alguém saia clonando todos os meus repositórios achando que virou produto 1.0: a maior parte dos projetos &lt;code&gt;frank-*&lt;/code&gt; no meu GitHub são provas de conceito. São experimentos. Alguns funcionam bem pra mim, no meu ambiente, no meu fluxo. Isso não quer dizer que estão prontos pra virar dependência crítica da sua empresa amanhã de manhã. Quer brincar, contribuir, estudar, ótimo. Só não confunda &amp;ldquo;publiquei no GitHub&amp;rdquo; com &amp;ldquo;está production ready&amp;rdquo;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>No post da semana passada, <a href="/2026/05/14/terminando-maratona-ia-sucesso-ou-fracasso/">&ldquo;Terminando a Maratona de IA: Sucesso ou Fracasso?&rdquo;</a>, eu encerrei a primeira fase da minha maratona. Mais de 600 horas de uso intenso de agentes de código, dezenas de projetos, centenas de milhares de linhas de código e documentação, e uma pilha de conclusões práticas. Não de marketing. Práticas.</p>
<p>Vou repetir uma coisa importante antes que alguém saia clonando todos os meus repositórios achando que virou produto 1.0: a maior parte dos projetos <code>frank-*</code> no meu GitHub são provas de conceito. São experimentos. Alguns funcionam bem pra mim, no meu ambiente, no meu fluxo. Isso não quer dizer que estão prontos pra virar dependência crítica da sua empresa amanhã de manhã. Quer brincar, contribuir, estudar, ótimo. Só não confunda &ldquo;publiquei no GitHub&rdquo; com &ldquo;está production ready&rdquo;.</p>
<p>Existem exceções. O <a href="https://github.com/akitaonrails/frankmd"target="_blank" rel="noopener">FrankMD</a>, por exemplo, recebeu uma quantidade decente de contribuições, testes, lapidação, correções de bugs reais, uso real. Hoje ele já é bem mais estável do que era no primeiro post. Ainda assim, o ponto continua: esses projetos nasceram como laboratório de vibe coding, não como plano de produto de 3 anos com roadmap, suporte enterprise e SLA.</p>
<p>Este post é um apanhado do que aprendi nesse laboratório. Algumas ferramentas minhas, alguns hábitos, algumas recomendações. Nada místico. Nenhuma &ldquo;nova forma revolucionária de programar&rdquo;. É engenharia velha com ferramenta nova.</p>
<h2>Primeiro: pare de colecionar agente<span class="hx:absolute hx:-mt-20" id="primeiro-pare-de-colecionar-agente"></span>
    <a href="#primeiro-pare-de-colecionar-agente" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você é desenvolvedor normal, você não precisa de 12 agentes, 40 wrappers, 7 orquestradores e um diagrama de Kubernetes pra escolher quem vai escrever um <code>if</code>.</p>
<p>Na prática, foque em <strong>Claude Code</strong>, <strong>Codex</strong> ou <strong>opencode</strong>. Só isso.</p>
<p>Sim, existe Gemini CLI. Sim, existe Cursor. Sim, existe Windsurf, Crush, OpenClaw, Pi, Aider, e amanhã vai aparecer mais meia dúzia. Eu testei um monte. Leia meus posts de benchmark de LLMs, especialmente o <a href="/2026/04/24/llm-benchmarks-parte-3-deepseek-kimi-mimo/">&ldquo;LLM Benchmarks Parte 3: DeepSeek, Kimi, MiMo&rdquo;</a>. A conclusão é chata e óbvia: hoje, as melhores opções de custo-benefício pra programação pesada são <strong>Opus 4.7</strong> e <strong>GPT 5.5</strong> nos planos Pro/Plus/Max subsidiados. Nada chega perto quando o assunto é trabalhar várias horas por dia com agente de código de verdade.</p>
<p>Pagar token direto é caro. Pagar plano mensal subsidiado sai muito mais barato. É por isso que eu vivo alternando entre Claude Code e Codex: não porque eu quero &ldquo;multi-agent architecture&rdquo;, mas porque os planos têm limites e eu trabalho muitas horas seguidas.</p>
<p>DeepSeek v4 e Kimi 2.6 são mais baratos? Sim. São usáveis? Sim. Eu gosto deles pra vários casos. Mas pra <strong>Agile Vibe Coding</strong> do jeito que venho defendendo desde o post <a href="/2026/02/20/do-zero-a-pos-producao-em-1-semana-como-usar-ia-em-projetos-de-verdade-bastidores-do-the-m-akita-chronicles/">&ldquo;Do Zero à Pós-Produção em 1 Semana&rdquo;</a>, ainda falta. Eles tropeçam mais em continuidade, em refatoração longa, em testes, em entender o projeto inteiro. Dá pra usar? Dá. Mas se o objetivo é produtividade consistente, Opus e GPT ainda são a régua.</p>
<p>Rodar modelo open source local é legal. Eu mesmo brinquei bastante com isso no review do <a href="/2026/03/31/review-minisforum-ms-s1-max-amd-ai-max-395/">Minisforum MS-S1 Max com AMD AI Max+ 395</a>. Pra one-shot, pra estudar, pra brincar com inferência local, é divertido. Pra Agile Vibe Coding o dia inteiro, todo dia, ainda não é prático. Latência, contexto, qualidade, manutenção do setup, VRAM, tudo cobra imposto.</p>
<p>Também não é prático ficar rodando múltiplos LLMs em paralelo, tipo &ldquo;planner + executor + reviewer + juiz&rdquo;. Eu testei isso no post <a href="/2026/04/25/llm-benchmarks-vale-a-pena-misturar-2-modelos/">&ldquo;LLM Benchmarks: Vale a Pena Misturar 2 Modelos?&rdquo;</a>. A ideia é bonita no papel. Na prática, aumenta custo, aumenta latência, aumenta superfície de erro, e o ganho não paga a complexidade.</p>
<p>O segredo não é ter um monte de agentes. Esquece OpenClaw ou Pi pra programação do dia a dia. O segredo é processo de engenharia. XP. Testes. <a href="/2026/04/20/clean-code-para-agentes-de-ia/">Clean Code pra agentes de IA</a>. CI. Pair programming. Refatoração constante. Código pequeno. Deploy automatizado. Feedback rápido.</p>
<p>Ferramenta ajuda. Engenharia ganha.</p>
<h2>ai-jail: autonomia com cerca<span class="hx:absolute hx:-mt-20" id="ai-jail-autonomia-com-cerca"></span>
    <a href="#ai-jail-autonomia-com-cerca" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O primeiro projeto que quero destacar é o <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a>. Ele é um wrapper de sandbox pra rodar agentes de IA com acesso restrito ao sistema. No Linux usa <code>bubblewrap</code> (<code>bwrap</code>) e Landlock. No macOS usa <code>sandbox-exec</code>. A ideia é simples: o agente enxerga o ambiente que precisa pra trabalhar, mas não sai fuçando seu sistema inteiro.</p>
<p>Uso todo dia? Sim. Uso sempre? Não.</p>
<p>Quando sei que não vou fazer nada arriscado, abro <code>claude</code> ou <code>codex</code> direto. Se vou mexer num projeto simples, só rodar testes, editar markdown, refatorar coisa local, muitas vezes nem me preocupo. Em mais de 600 horas de Agile Vibe Coding intenso, mais de meio milhão de linhas de código e documentação, nunca tive um caso desses folclóricos de LLM destruindo meu sistema host de um jeito irrecuperável.</p>
<p>E olha que eu usei agentes explicitamente pra configurar meu Omarchy. ZSH, Hyprland, Waybar, configs de terminal, scripts de login, tudo. Nunca fizeram nada que eu não pedi.</p>
<p>Essas histórias de &ldquo;o agente deletou meu home inteiro sozinho&rdquo; eu leio com um pé atrás. Na minha experiência, quando você pede pra deletar algo perigoso, Claude e Codex perguntam confirmação. Você precisa dizer sim. Se você disse sim sem ler, a culpa não é mágica negra do LLM. É usuário no modo zumbi.</p>
<p>Dito isso: nada é garantido. Daí o ai-jail.</p>
<p>Meu uso preferido é combinar <code>ai-jail</code> com o modo sem freio do harness:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail claude --dangerously-allow-permissions
</span></span><span class="line"><span class="cl">ai-jail codex --dangerously-bypass-approvals-and-sandbox</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Parece contraditório, mas faz sentido. Eu tiro a fricção de confirmação dentro do agente, mas coloco uma cerca no nível do sistema operacional. O agente pode trabalhar rápido dentro do projeto, mas o filesystem do host fica essencialmente read-only. O diretório atual fica read-write. O que eu mapear explicitamente com <code>--rw-map</code> também fica read-write. O resto não é pra tocar.</p>
<p>O README do ai-jail resume bem: é um sandbox wrapper pra agentes como Claude Code, GPT Codex, OpenCode e Crush. No primeiro run ele cria um <code>.ai-jail</code> no projeto, que pode ser commitado. Suporta <code>--dry-run</code> pra ver os mounts antes de executar, <code>--lockdown</code> pra modo mais restritivo, <code>--private-home</code> pra esconder dotfiles reais, <code>--mask .env</code> pra esconder arquivos sensíveis dentro do próprio projeto, e perfis de navegador isolado pra Chromium/Firefox.</p>
<p>O ponto importante: não venda isso como segurança absoluta. Processo sandbox não é VM. Kernel bug existe. Side channel existe. macOS tem limitações. O próprio README é claro: se você precisa isolar workload hostil de verdade, use uma VM descartável. Eu não uso ai-jail como &ldquo;blindagem militar&rdquo;. Uso como mais uma camada, boa o suficiente pro fluxo de desenvolvimento.</p>
<p>E mesmo dentro do projeto, tudo é Git. Eu faço push frequente pro GitHub ou pro meu Gitea privado. Então o pior caso realista é o agente destruir o diretório do projeto. Chato? Sim. Catastrófico? Não. Clono de novo. Volto do remote. Refaço o que estava local. Se o <code>.git</code> corromper, apago tudo e clono de novo.</p>
<p>Secrets também têm cópia fora. <code>.env</code>, tokens, keys, tudo que importa fica no Bitwarden. <a href="/2025/09/10/omarchy-2-0-bitwarden-self-hosted-vaultwarden/">Self-hosted em Vaultwarden</a>, claro. Então se eu perder o diretório local, não perdi a chave do reino.</p>
<p>O risco mais comum não é o agente deletar tudo. É você vazar segredo no Git. Isso aconteceu comigo mais de uma vez. Você dá <code>git add .</code>, esquece um <code>.env</code>, empurra pro remote, parabéns. Acontece. Por isso tem que olhar <code>git status</code>, <code>git diff --cached</code>, e pensar antes de dar push.</p>
<p>Mesmo quando acontece, os LLMs são muito competentes pra ajudar a reescrever histórico. Peço pra limpar o segredo do Git, forçar push, revisar se sumiu, e depois eu rotaciono a key. Não é bonito, mas é administrável.</p>
<p>Então sim, recomendo usar Claude/Codex com permissões perigosas <strong>dentro</strong> do ai-jail. Existe a chance de 0,001% de acidente irrecuperável? Existe. Mas se você é bom engenheiro, isso não deveria te matar. Eu tenho camadas: snapshots automáticos em Btrfs, restic pro NAS, NAS arquivando offsite em AWS Glacier, Git remoto, Bitwarden. Ferramenta nenhuma substitui backup.</p>
<h2>ai-memory: contexto que sobrevive ao harness<span class="hx:absolute hx:-mt-20" id="ai-memory-contexto-que-sobrevive-ao-harness"></span>
    <a href="#ai-memory-contexto-que-sobrevive-ao-harness" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O segundo é o <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a>, que expliquei em detalhes no post <a href="/2026/05/23/criei-sistema-memoria-agentes-codigo-ai-memory/">&ldquo;Criei um Sistema de Memória pra Agentes de Código: ai-memory&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ai-memory/ai-memory-logo.png" alt="Logo do projeto ai-memory: pictograma circular com um chip estilizado abrindo “asas” tipo bibliotecas/páginas pra cada lado, ao lado do texto ai-memory em itálico."  loading="lazy" /></p>
<p>A motivação é simples: harnesses perdem contexto. Claude Code compacta. Codex compacta. opencode compacta. Sessão acaba. Você abre outro agente e ele não sabe o que aconteceu nas últimas 4 horas. Se você usa só um harness, talvez isso não incomode tanto. Se você alterna entre Claude e Codex como eu, incomoda rápido.</p>
<p>Eu queria um consolidador de memória de longo prazo que funcionasse atravessando harnesses diferentes. Algo que permitisse handoff entre Claude, Codex e opencode sem eu ficar escrevendo <code>HANDOFF.md</code> manual toda vez. Algo que capturasse prompts, tool calls e eventos de sessão via hooks, consolidasse em páginas markdown, indexasse com SQLite/FTS5, e expusesse tudo via MCP quando o agente precisasse consultar.</p>
<p>Outro motivador foi o que expliquei no post <a href="/2026/04/06/rag-esta-morto-contexto-longo/">&ldquo;RAG Está Morto? Contexto Longo, Grep e o Fim do Vector DB Obrigatório&rdquo;</a>. Pra projeto de código, quase sempre texto simples resolve mais do que arquitetura afetada de vector DB. Markdown, <code>grep</code>, SQLite FTS5, contexto longo e bons arquivos em <code>docs/</code> já levam você longe. O ai-memory segue essa linha: compila memória em texto, deixa no disco, indexa simples, e só chama LLM quando vale consolidar.</p>
<p>O ai-memory não foi feito pra ser uma knowledge base navegável por usuário final. Ele tem uma UI web read-only, sim, mas isso é pra auditoria e inspeção. A memória é otimizada pra LLM consumir. É um wiki interno que o harness pode consultar quando acha que precisa. É um lugar onde posso pedir explicitamente &ldquo;salva isso pra próxima sessão&rdquo; e esperar que sobreviva mais do que a compactação padrão do Claude ou do Codex.</p>
<p><img src="https://raw.githubusercontent.com/akitaonrails/ai-memory/main/docs/web-projects-home.png" alt="Homepage da UI web do ai-memory mostrando projetos detectados, contagem de páginas, última atividade e o aviso de que a memória é otimizada pra consumo por LLM, não documentação curada por humanos."  loading="lazy" /></p>
<p>O README resume bem: agentes de código perdem todo contexto quando a sessão termina; o ai-memory dá a eles um wiki persistente compartilhado. Cada prompt, tool call e decisão pode ser capturado automaticamente. No fim da sessão, páginas relevantes são reescritas como narrativa coerente. Na próxima sessão, outro agente pode receber um handoff já prepended no contexto.</p>
<p>Isso é útil quando você fecha Claude às 18h, abre Codex no dia seguinte no mesmo diretório, e quer que ele saiba que você estava testando session cookies porque rotação de JWT tinha dado problema. Ou quando você volta a um projeto seis semanas depois e não lembra por que descartou Redis.</p>
<p>Mas não confunda com documentação canônica. Se você fez uma pesquisa importante que não quer perder, peça pro agente escrever um markdown em <code>docs/</code> dentro do projeto. Decisão arquitetural, benchmark, análise de biblioteca, runbook de deploy, tudo que precisa sobreviver com garantia deve virar arquivo explícito no repo. Isso é mais simples, auditável e versionado.</p>
<p>O ai-memory cobre a faixa intermediária: informação transitória que talvez importe depois. Um bug estranho que apareceu. Um raciocínio que levou a uma decisão. Um gotcha de ambiente. Um resumo de sessão. Coisas que você não quer transformar em doc oficial ainda, mas também não quer jogar fora.</p>
<p>Ele ainda é beta. Eu uso em produção pessoal, mas precisa de muito mais gente batendo nele. Tem integração com Claude Code, Codex e opencode mais polida. Gemini CLI, OpenClaw e clientes MCP exóticos ainda precisam de mais teste. PRs são bem-vindos. Bug report também. &ldquo;Instalei e quebrou no meu setup&rdquo; é exatamente o tipo de feedback que revela suposição errada.</p>
<h2>ai-usagebar: saber quando trocar de cavalo<span class="hx:absolute hx:-mt-20" id="ai-usagebar-saber-quando-trocar-de-cavalo"></span>
    <a href="#ai-usagebar-saber-quando-trocar-de-cavalo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Como falei acima, eu prefiro usar os planos subsidiados da Anthropic e da OpenAI. O problema é que, se você faz Agile Vibe Coding de verdade, você bate limite. Limite diário. Limite semanal. Limite de sessão. Limite que muda. Limite que não aparece direito em lugar nenhum.</p>
<p>Eu usava o <a href="https://github.com/mryll/claudebar"target="_blank" rel="noopener">claudebar</a>, que é bom, mas só cobre Claude. Eu queria ver os principais vendors num lugar só. Daí nasceu o <a href="https://github.com/akitaonrails/ai-usagebar"target="_blank" rel="noopener">ai-usagebar</a>, que apresentei no post <a href="/2026/05/24/criei-widget-waybar-omarchy-monitorar-uso-llms-ai-usagebar/">&ldquo;Criei um Widget de Waybar pra Omarchy pra Monitorar Uso de Planos de LLM: ai-usagebar&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-usagebar/ai-usagebar-waybar.png" alt="Widget na Waybar mostrando “cld 29% · 1h 12m” no canto superior direito, com tooltip aberto exibindo o plano Claude Max 20x: Session 29%, Weekly 47%, Sonnet only 0%, Extra usage $0.00 sobre limite de $1100, e horário da última atualização."  loading="lazy" /></p>
<p>É um widget de Waybar e uma TUI em Rust pra monitorar uso de Anthropic Claude, OpenAI Codex/ChatGPT, Z.AI GLM e OpenRouter. Pra Claude e OpenAI, ele lê as credenciais OAuth que os CLIs oficiais já gravaram. Pra Z.AI e OpenRouter, usa API key. Mostra porcentagem da sessão, reset, limites, saldo, e permite trocar o vendor principal.</p>
<p>O valor disso é banal e por isso mesmo útil: bato o olho na barra e sei se devo continuar no Claude, fazer handoff pro Codex, ou recarregar crédito no OpenRouter. Não quero descobrir que cheguei no limite semanal no meio de uma refatoração grande.</p>
<p>Também tem a TUI standalone:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-usagebar/ai-usagebar-tui-openai.png" alt="ai-usagebar-tui na aba OpenAI rodando num terminal: gauges de Codex 5h e semanal, bloco de Credits com faixas de contagem de mensagens, abas Claude / OpenAI / GLM (Z.AI) / OpenRouter no topo, atalhos de tecla no rodapé."  loading="lazy" /></p>
<p>Se você não usa Omarchy, Hyprland ou Waybar, ainda dá pra rodar <code>ai-usagebar-tui</code> em qualquer terminal. SSH, tmux, terminal normal, tanto faz. A barra é conveniência. A TUI é a forma universal.</p>
<h2>ghpending: pendência de GitHub sem ritual<span class="hx:absolute hx:-mt-20" id="ghpending-pendência-de-github-sem-ritual"></span>
    <a href="#ghpending-pend%c3%aancia-de-github-sem-ritual" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Depois da maratona, fiquei com dezenas de <code>ai-*</code> e <code>frank-*</code> no GitHub. Isso cria um problema chato: issues e pull requests começam a aparecer. Contribuidor abre PR. Alguém reporta bug. Outro manda sugestão. Se eu tiver que abrir o navegador, entrar repo por repo, filtrar aba por aba, eu vou adiar. E se eu adiar, fico semanas sem responder.</p>
<p>Sim, eu poderia usar <code>gh</code>, a CLI oficial do GitHub. Aliás, uso. Mas eu queria uma coisa mais burra e mais rápida: um comando curto pra imprimir um digest de tudo que está pendente nos repos que eu escolhi monitorar. Nasceu o <a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener">ghpending</a>, que descrevi no post <a href="/2026/05/23/criei-cli-pra-checar-pendencias-github-ghpending/">&ldquo;Criei um CLI pra Checar Pendências no Meu GitHub: ghpending&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ghpending/ghpending-screenshot.png" alt="Saída do ghpending no terminal: tabela compacta listando cada repo monitorado com contagem de issues e pull requests abertos, autor da última atividade e há quanto tempo."  loading="lazy" /></p>
<p>É só isso: <code>ghpending add</code> pra escolher repos, <code>ghpending</code> pra ver o digest, <code>ghpending list</code>, <code>ghpending rm</code>. Sem autenticação funciona com limite de 60 req/h do GitHub. Com <code>GITHUB_TOKEN=$(gh auth token) ghpending</code>, sobe pra 5.000 req/h.</p>
<p>O ponto não é substituir o GitHub. É reduzir atrito. Se eu consigo rodar um comando no terminal e ver &ldquo;tem 3 PRs e 2 issues pra olhar&rdquo;, eu olho. Se depende de ritual de navegador, notificações e abas, fica pra depois.</p>
<p>E Claude/Codex são muito bons nesse tipo de trabalho. Dá pra pedir:</p>
<blockquote>
  <p>&ldquo;Checa os pull requests abertos, revisa o código, vê se compila, roda os testes, adiciona teste se faltar, faz rebase de master se precisar, mergeia e fecha.&rdquo;</p>

</blockquote>
<p>Ou:</p>
<blockquote>
  <p>&ldquo;Checa as issues abertas, avalia se tem bug real, reproduz se der, propõe correção, responde o usuário.&rdquo;</p>

</blockquote>
<p>Eles leem o PR, entendem o diff, rodam teste, fazem rebase, resolvem conflito simples, escrevem resposta pro contribuidor. Não é pra desligar o cérebro. É pra terceirizar o trabalho mecânico que você já sabe fazer.</p>
<h2>Refatoração: peça explicitamente, várias vezes<span class="hx:absolute hx:-mt-20" id="refatoração-peça-explicitamente-várias-vezes"></span>
    <a href="#refatora%c3%a7%c3%a3o-pe%c3%a7a-explicitamente-v%c3%a1rias-vezes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Claude e Codex são muito bons em refatorar, mas não presuma que vão fazer isso automaticamente do jeito que você faria. Eu costumo começar projetos ou features com instruções explícitas:</p>
<blockquote>
  <p>&ldquo;Use as melhores práticas de Rust. Evite duplicação desnecessária. Modularize só onde fizer sentido. Adicione unit tests pra toda feature nova. Siga princípios de <a href="/2026/04/20/clean-code-para-agentes-de-ia/">Clean Code</a>. Não crie abstração antes de precisar.&rdquo;</p>

</blockquote>
<p>Depois de uma feature grande, uma correção grande, ou algumas horas de desenvolvimento, paro e peço:</p>
<blockquote>
  <p>&ldquo;Agora que mexemos bastante no código, reavalie com calma. Delete código morto. Refatore duplicações óbvias. Adicione testes faltantes. Remova magic numbers. Veja se tem módulos grandes demais. Não mude comportamento sem teste.&rdquo;</p>

</blockquote>
<p>Antes de release:</p>
<blockquote>
  <p>&ldquo;Antes de publicar essa versão, faça uma auditoria de sanidade. Procure vazamento de secrets, permissões perigosas, comandos destrutivos, path traversal, unwraps desnecessários, falta de validação, e qualquer coisa que pareça vulnerabilidade óbvia.&rdquo;</p>

</blockquote>
<p>Isso garante segurança? Claro que não. Nem perto. Mas remove muito erro besta. É mais uma camada. Eu gosto de rodar essa auditoria com Claude e depois com Codex, porque cada um acha coisas diferentes. Mesmo assim, bug passa. Segurança é processo, não ritual mágico.</p>
<h2>Experimentos descartáveis: código ficou barato<span class="hx:absolute hx:-mt-20" id="experimentos-descartáveis-código-ficou-barato"></span>
    <a href="#experimentos-descart%c3%a1veis-c%c3%b3digo-ficou-barato" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de escolher biblioteca, framework, arquitetura, modelo ou serviço, eu gosto de criar <code>experiments/</code>.</p>
<p>Exemplo:</p>
<blockquote>
  <p>&ldquo;Antes de escolher entre Qwen e DeepSeek, cria um protótipo descartável em <code>experiments/</code> pra A/B test. Documenta a análise em <code>docs/</code> com resultado, custo, latência, qualidade e recomendação.&rdquo;</p>

</blockquote>
<p>Isso mudou meu jeito de decidir. Antes, muita decisão de engenharia virava opinião, feeling, fé em benchmark dos outros. Agora código é barato. Peço pro agente fazer duas ou três versões, rodar teste, medir, documentar, jogar fora o que não presta.</p>
<p>Prototipar ficou rápido de verdade. O <a href="/2026/04/05/transformando-youtube-num-app-de-karaoke-frank-karaoke/">Frank Karaoke</a> é um bom exemplo: transformar YouTube num app de karaokê era o tipo de ideia que antes eu talvez deixasse numa lista mental por meses, esperando &ldquo;um fim de semana livre&rdquo; que nunca chega. Agora dá pra abrir um <code>experiments/</code>, pedir um protótipo tosco, testar o caminho técnico, e decidir se vale virar projeto. Olha meus outros posts de 2026 sobre os projetos <code>frank-*</code> e <code>ai-*</code>: quase todos começaram assim, como código descartável que provou alguma coisa antes de merecer nome.</p>
<p>Mas protótipo barato não significa one-shot. Já escrevi em <a href="/2026/04/15/como-falar-com-o-claude-code-efetivamente/">&ldquo;Por que as LLMs não te dão o resultado esperado&rdquo;</a>: prompt bom é conversa iterativa. Você dá contexto, mostra referência, aponta restrição, lê o resultado, corrige direção, manda refazer. Se você joga uma frase vaga e espera produto pronto, o erro é seu.</p>
<p>Também aprendi isso apanhando. No post do <a href="/2026/03/05/meu-primeiro-fracasso-com-vibe-code-e-como-consertei-frank-yomik/">Frank Yomik</a> contei meu primeiro fracasso real com vibe coding: quando a direção está errada, insistir no mesmo prompt só cava o buraco. Tem hora que precisa reduzir escopo, isolar o problema, criar teste, trocar estratégia. LLM não transforma plano ruim em engenharia boa.</p>
<p>E tem domínio onde você precisa estudar junto. No post de <a href="/2026/04/23/driveclub-shadps4-e-ia-como-aprender/">Driveclub e shadPS4</a>, a IA quase me levou pra todo lado errado porque o problema exigia entender renderização, gamma, shaders, buffers. A ferramenta ajuda a explorar, mas quem decide se a explicação faz sentido ainda é você. Se você não entende o suficiente pra verificar, está só aceitando chute bonito.</p>
<p>Por isso também repito a ideia de <a href="/2026/03/01/software-nunca-esta-pronto-4-projetos-a-vida-pos-deploy-e-por-que-one-shot-prompt-e-mito/">&ldquo;Software Nunca Está Pronto&rdquo;</a>: deploy não é linha de chegada. O protótipo vira produto quando aguenta bug report, manutenção, refatoração, docs, release, rollback, usuário reclamando. LLM ajuda muito nesse ciclo chato. Mas o ciclo continua existindo.</p>
<p>Se é testável, teste. Se dá pra medir, meça. Se não dá pra medir, pelo menos escreva as premissas. O importante é não escolher só porque &ldquo;parece melhor&rdquo;.</p>
<p>Código barato também significa desapego. Faça código pra jogar fora. Faça protótipo feio. Faça benchmark descartável. Peça pro agente documentar o que aprendeu em <code>docs/decision-qwen-vs-deepseek.md</code>, depois apague <code>experiments/</code> se quiser. O documento fica. O aprendizado fica.</p>
<p>LLMs também são muito bons em documentação. Use isso. Peça pra documentar tudo. Por que escolhemos PostgreSQL em vez de SQLite nesse caso. Por que o deploy usa Docker image multi-stage. Por que o serviço roda atrás de Cloudflare Tunnel. Por que aquele sysctl existe. Mesmo que depois você delete metade, o custo de escrever caiu perto de zero.</p>
<h2>Deploy automatizado ou você vai parar de manter<span class="hx:absolute hx:-mt-20" id="deploy-automatizado-ou-você-vai-parar-de-manter"></span>
    <a href="#deploy-automatizado-ou-voc%c3%aa-vai-parar-de-manter" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todo projeto meu que importa tem build automatizado. Muitos rodam no meu homelab, como descrevi em <a href="/2026/03/31/migrando-meu-home-server-com-claude-code/">&ldquo;Migrando meu Home Server com Claude Code&rdquo;</a>. Quase todo projeto tem um <code>bin/deploy</code> que builda imagem Docker, faz push, rotaciona no servidor, aplica migração se precisar, e valida healthcheck.</p>
<p>Claude e Codex são excelentes em deploy. Eles leem o <code>bin/deploy</code>, entendem Dockerfile, editam GitHub Actions, ajustam release workflow, corrigem assinatura de binário, atualizam Homebrew tap, geram checksum. E sim, eu peço pra Claude/Codex dar SSH no meu homelab e redeployar container Docker. Eles são muito bons nisso. O ai-jail, por exemplo, tem GitHub Actions pra buildar binários assinados pra Linux e macOS.</p>
<p>Se você tem dezenas de projetos, quanto mais automatizado melhor. Eu raramente digito comando administrativo no terminal sozinho. Peço pro LLM fazer, observo, aprovo o que for perigoso, e depois peço:</p>
<blockquote>
  <p>&ldquo;Documente o que foi feito e por que foi feito.&rdquo;</p>

</blockquote>
<p>Isso vale pro homelab e pra minha workstation. Meu Omarchy é customizado via Claude: ZSH, Hyprland, Waybar, scripts, atalhos, temas, integrações. Até meus setups de gaming/emulação entram nessa, como mostrei no post dos <a href="/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/">retrogames de corrida rodando no Distrobox</a>. Esse é um caso onde normalmente não uso ai-jail, porque justamente quero mexer no host. Então olho comandos, leio diffs, aprovo devagar. Mesmo assim, nunca tive o agente quebrando meu sistema de um jeito irrecuperável. Pelo contrário. Meu PC nunca esteve tão estável e documentado.</p>
<p>Dias depois, quando esqueço por que configurei alguma coisa daquele jeito, o contexto está em markdown, Git, ai-memory, ou no próprio histórico do projeto. Eu posso perguntar e o agente tem de onde puxar.</p>
<h2>Até este post foi escrito assim<span class="hx:absolute hx:-mt-20" id="até-este-post-foi-escrito-assim"></span>
    <a href="#at%c3%a9-este-post-foi-escrito-assim" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Inclusive, é assim que escrevo estes posts agora. Eu jogo um bloco enorme e bagunçado do meu cérebro no Claude. Peço pra ler posts recentes pra calibrar estilo. Peço pra checar os projetos diretamente e elaborar as partes técnicas. Peço pra usar as screenshots mais recentes, subir pro S3 quando precisa, linkar os posts anteriores, revisar o tom.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-toolkit/writing-this-post.png" alt="Screenshot do terminal com o prompt bruto usado pra escrever este post: um bloco enorme de instruções, links, opiniões e detalhes técnicos jogado no agente antes da edição final."  loading="lazy" /></p>
<p>Depois eu leio e edito. Corto o que ficou genérico. Troco frase que parece release note. Coloco minhas broncas de volta. O resultado ainda é meu texto, só que agora eu gasto energia no que importa: decidir o que dizer, corrigir nuance, cortar besteira.</p>
<h2>&ldquo;Mas você testou o Pi?&rdquo;<span class="hx:absolute hx:-mt-20" id="mas-você-testou-o-pi"></span>
    <a href="#mas-voc%c3%aa-testou-o-pi" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Toda vez que falo de LLM ou ferramenta de LLM, aparece alguém nos comentários com a mesma pergunta: &ldquo;mas você testou Pi?&rdquo;, &ldquo;mas você testou DeepSeek?&rdquo;, &ldquo;mas e o Qwen?&rdquo;, &ldquo;mas e o modelo novo que saiu ontem?&rdquo;. Toda. Santa. Vez.</p>
<p>É irritante pra cacete. E quase sempre vem de três tipos de pessoa: quem não testou nada e quer terceirizar o trabalho, quem já escolheu uma ferramenta e quer que eu valide a escolha dela, ou fanboy que trata vendor de LLM como time de futebol. Todos soam iguais. NPC repetindo diálogo de tutorial.</p>
<p>Engenheiro de software não deveria decidir baseado em opinião dos outros. Nem na minha. Se você escolhe ferramenta porque &ldquo;o Akita falou&rdquo;, você entendeu errado. Eu defendo Claude Code e Codex porque usei centenas de horas, em projetos reais, publicando código e post-mortem do que funcionou e do que falhou. E mesmo assim eu não parei em opinião.</p>
<p>Eu criei meu próprio benchmark automatizado: <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">llm-coding-benchmark</a>. Atualizo quando vendor popular solta versão nova. Rodo cenários comparáveis. Publico resultado. Depois ajusto minha opinião baseado nos dados. É por isso que eu tenho segurança pra dizer que, no meu fluxo, Opus e GPT continuam vencendo. Não porque acordei com vontade de defender logo de empresa bilionária.</p>
<p>Se sua primeira reação foi &ldquo;mas você testou Qwen?&rdquo;, ótimo. Testa você. Roda o benchmark. Cria cenário melhor. Manda PR. Mostra número. Mostra custo. Mostra latência. Mostra onde falhou. Mostra onde ganhou. Traga dado novo.</p>
<p>Opinião solta é irrelevante. &ldquo;Pra mim funciona&rdquo; não escala. &ldquo;Eu gosto mais&rdquo; não interessa. &ldquo;No meu prompt one-shot ele foi melhor&rdquo; é anedota. Engenharia começa quando dá pra reproduzir, comparar e agir em cima.</p>
<p>Então fica simples: traga dado acionável ou fique quieto. Eu não preciso de torcida organizada de vendor nos comentários. Preciso de gente testando, medindo e contribuindo resultado novo.</p>
<h2>Checklist final<span class="hx:absolute hx:-mt-20" id="checklist-final"></span>
    <a href="#checklist-final" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Resumo curto, porque este post já ficou grande:</p>
<ul>
<li>Foque em Claude Code, Codex ou opencode. Pare de colecionar agente.</li>
<li>Use Opus 4.7 e GPT 5.5 via planos Pro/Plus/Max quando produtividade diária importa.</li>
<li>DeepSeek, Kimi e modelos locais são úteis, mas ainda não substituem o fluxo principal.</li>
<li>Agile Vibe Coding é XP com LLM: testes, <a href="/2026/04/20/clean-code-para-agentes-de-ia/">Clean Code</a>, CI, pair programming e deploy.</li>
<li>Use <code>ai-jail</code> quando quiser autonomia do agente com cerca no filesystem.</li>
<li>Combine <code>ai-jail</code> com permissões perigosas do Claude/Codex quando o projeto estiver protegido por Git.</li>
<li>Tenha backup de verdade: Btrfs snapshots, restic, NAS, offsite, Git, Bitwarden.</li>
<li>Revise <code>git add</code>, <code>git diff --cached</code> e pushes pra não vazar secrets.</li>
<li>Use <code>ai-memory</code> pra contexto transitório e handoff entre agentes.</li>
<li>Escreva documentação canônica em <code>docs/</code> quando a informação precisa sobreviver com garantia.</li>
<li>Use <code>ai-usagebar</code> pra saber quando trocar de Claude pra Codex, ou quando recarregar OpenRouter.</li>
<li>Use <code>ghpending</code> pra não abandonar issues e PRs.</li>
<li>Peça refatoração explicitamente depois de features grandes.</li>
<li>Rode auditoria de segurança antes de release, de preferência com mais de um modelo.</li>
<li>Crie protótipos descartáveis em <code>experiments/</code> antes de decidir por fé.</li>
<li>Se discordar de benchmark, rode o <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">llm-coding-benchmark</a> e traga dado novo.</li>
<li>Documente decisões em markdown. Código ficou barato. Documentação também.</li>
<li>Automatize deploy. Se depender de ritual manual, você vai parar de fazer.</li>
</ul>
<p>No fim, é o mesmo conselho de sempre: ferramenta muda, engenharia não. LLM aumenta a alavanca. Se você já tinha bons hábitos, vai produzir mais. Se não tinha, vai só produzir bagunça mais rápido.</p>
<p>E sim, isso muda a interface do trabalho. Já escrevi sobre isso em <a href="/2026/04/11/vs-code-e-o-novo-cartao-perfurado/">&ldquo;VS Code é o novo Cartão Perfurado&rdquo;</a>: ficar polindo pixel em editor virou nostalgia de programador velho. A parte difícil agora é escrever intenção direito, revisar execução sem preguiça, medir resultado, jogar fora código barato, e não confundir opinião com dado.</p>
]]></content:encoded><category>ai-jail</category><category>ai-memory</category><category>ai-usagebar</category><category>ghpending</category></item><item><title>ai-jail: Sandbox para Agentes de IA — De Shell Script a Ferramenta Real</title><link>https://akitaonrails.github.io/2026/03/01/ai-jail-sandbox-para-agentes-de-ia-de-shell-script-a-ferramenta-real/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/03/01/ai-jail-sandbox-para-agentes-de-ia-de-shell-script-a-ferramenta-real/</guid><pubDate>Sun, 01 Mar 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;Este post é um follow-up direto do &lt;a href="https://akitaonrails.github.io/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/"&gt;AI Agents: Garantindo a Proteção do seu Sistema&lt;/a&gt;, onde eu mostrei como usar bubblewrap pra criar um jail manual pros seus agentes de IA. Se não leu, leia antes de continuar.&lt;/p&gt;
&lt;p&gt;&amp;ndash;&lt;/p&gt;
&lt;p&gt;Em janeiro publiquei um shell script de ~170 linhas que montava um sandbox com bubblewrap pra rodar Claude Code, OpenCode, Crush e qualquer outro agente de IA. Funcionava. Resolvia o problema. Mas era um script bash jogado em &lt;code&gt;~/.local/bin/&lt;/code&gt; que você tinha que copiar, colar, e rezar pra não precisar customizar demais.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este post é um follow-up direto do <a href="/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/">AI Agents: Garantindo a Proteção do seu Sistema</a>, onde eu mostrei como usar bubblewrap pra criar um jail manual pros seus agentes de IA. Se não leu, leia antes de continuar.</p>
<p>&ndash;</p>
<p>Em janeiro publiquei um shell script de ~170 linhas que montava um sandbox com bubblewrap pra rodar Claude Code, OpenCode, Crush e qualquer outro agente de IA. Funcionava. Resolvia o problema. Mas era um script bash jogado em <code>~/.local/bin/</code> que você tinha que copiar, colar, e rezar pra não precisar customizar demais.</p>
<p>Dois meses usando aquele script todo dia me mostraram os limites. Queria configuração por projeto. Queria suporte a macOS pros devs do meu time. Queria parar de editar arrays em bash toda vez que precisava de um diretório extra. E queria algo que alguém pudesse instalar com <code>brew install</code> ou <code>cargo install</code> em 10 segundos, sem ler 170 linhas de script.</p>
<p>Resultado: <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a>. Uma ferramenta em Rust, ~880KB, 4 dependências, 124 testes, que faz exatamente o que aquele script fazia e mais. Vou explicar o que mudou e por que você deveria estar usando.</p>
<h2>O Problema (de novo, pra quem pulou o post anterior)<span class="hx:absolute hx:-mt-20" id="o-problema-de-novo-pra-quem-pulou-o-post-anterior"></span>
    <a href="#o-problema-de-novo-pra-quem-pulou-o-post-anterior" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agentes de IA pra programação precisam de acesso ao seu filesystem. Precisam rodar compiladores, linters, grep, ls, make, cargo, npm. O mínimo pra serem úteis. O problema é que junto com esse acesso vem a capacidade de ler <code>~/.aws/credentials</code>, exportar suas chaves SSH, ou rodar um <code>rm -rf</code> fora do diretório do projeto.</p>
<p>Não é paranoia. Supply-chain attacks são reais. Toda hora uma lib de NPM, PyPI, ou RubyGems é comprometida. Se o agente rodar <code>npm install</code> e um post-install script malicioso tentar exfiltrar seus dados, a única coisa entre o atacante e suas credenciais é o que você montou de barreira antes.</p>
<p>A resposta é um sandbox. Especificamente, um que permita o agente trabalhar no diretório do projeto com as ferramentas que precisa, mas que torne invisível todo o resto do sistema.</p>
<h2>De Script pra Ferramenta<span class="hx:absolute hx:-mt-20" id="de-script-pra-ferramenta"></span>
    <a href="#de-script-pra-ferramenta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O shell script do post anterior já resolvia isso com bubblewrap. O ai-jail resolve o mesmo problema, mas trata as limitações que dois meses de uso diário revelaram:</p>
<table>
  <thead>
      <tr>
          <th>Shell script</th>
          <th>ai-jail</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Configuração editando arrays bash</td>
          <td>Arquivo <code>.ai-jail</code> TOML por projeto</td>
      </tr>
      <tr>
          <td>Só Linux</td>
          <td>Linux + macOS</td>
      </tr>
      <tr>
          <td>GPU/Docker/Display hardcoded</td>
          <td>Auto-detecção com flags pra desligar</td>
      </tr>
      <tr>
          <td>Sem dry-run</td>
          <td><code>--dry-run --verbose</code> mostra tudo</td>
      </tr>
      <tr>
          <td>Sem lockdown</td>
          <td><code>--lockdown</code> pra modo paranóico</td>
      </tr>
      <tr>
          <td>Copiar/colar pra instalar</td>
          <td><code>brew install</code>, <code>cargo install</code>, <code>mise</code></td>
      </tr>
      <tr>
          <td>Sem bootstrap</td>
          <td><code>--bootstrap</code> gera configs de permissão pro Claude/Codex/OpenCode</td>
      </tr>
  </tbody>
</table>
<p>A lógica core é a mesma: bubblewrap cria namespaces isolados de PID, UTS, IPC, monta o <code>$HOME</code> como tmpfs efêmero, e só monta o diretório do projeto com escrita. A diferença é que agora tudo isso é configurável sem editar código.</p>
<h2>Instalação<span class="hx:absolute hx:-mt-20" id="instalação"></span>
    <a href="#instala%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quatro formas:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Homebrew (macOS e Linux)</span>
</span></span><span class="line"><span class="cl">brew tap akitaonrails/tap <span class="o">&amp;&amp;</span> brew install ai-jail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Cargo</span>
</span></span><span class="line"><span class="cl">cargo install ai-jail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Mise</span>
</span></span><span class="line"><span class="cl">mise use -g ubi:akitaonrails/ai-jail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Binário direto do GitHub Releases</span>
</span></span><span class="line"><span class="cl">curl -fsSL https://github.com/akitaonrails/ai-jail/releases/latest/download/ai-jail-linux-x86_64.tar.gz <span class="p">|</span> tar xz
</span></span><span class="line"><span class="cl">sudo mv ai-jail /usr/local/bin/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No Linux, bubblewrap precisa estar instalado separadamente: <code>pacman -S bubblewrap</code> (Arch), <code>apt install bubblewrap</code> (Debian/Ubuntu), <code>dnf install bubblewrap</code> (Fedora). No macOS não precisa de nada extra.</p>
<h2>Uso Básico<span class="hx:absolute hx:-mt-20" id="uso-básico"></span>
    <a href="#uso-b%c3%a1sico" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ~/Projects/meu-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Roda Claude Code dentro do sandbox</span>
</span></span><span class="line"><span class="cl">ai-jail claude
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Roda Codex</span>
</span></span><span class="line"><span class="cl">ai-jail codex
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Roda OpenCode</span>
</span></span><span class="line"><span class="cl">ai-jail opencode
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Bash puro pra debug</span>
</span></span><span class="line"><span class="cl">ai-jail bash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Qualquer comando</span>
</span></span><span class="line"><span class="cl">ai-jail -- python script.py</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Na primeira execução, ele cria um arquivo <code>.ai-jail</code> no diretório do projeto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># ai-jail sandbox configuration</span>
</span></span><span class="line"><span class="cl"><span class="c"># Edit freely. Regenerate with: ai-jail --clean --init</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">command</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;claude&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">rw_maps</span> <span class="p">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="nx">ro_maps</span> <span class="p">=</span> <span class="p">[]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse arquivo é commitável no repo. Quando outro dev do seu time clona o projeto e roda <code>ai-jail</code>, a mesma configuração se aplica.</p>
<p>Se quiser adicionar diretórios extras, pode fazer pela CLI ou direto no TOML:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Diretório extra com escrita</span>
</span></span><span class="line"><span class="cl">ai-jail --rw-map ~/Projects/shared-lib claude
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Diretório extra somente leitura</span>
</span></span><span class="line"><span class="cl">ai-jail --map /opt/datasets claude</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quer ver o que o sandbox vai fazer sem executar nada?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail --dry-run --verbose claude</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso mostra cada mount point, cada flag de isolamento, o comando completo do bubblewrap. Sem surpresas.</p>
<h2>Por Que Bubblewrap no Linux<span class="hx:absolute hx:-mt-20" id="por-que-bubblewrap-no-linux"></span>
    <a href="#por-que-bubblewrap-no-linux" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Avaliei as alternativas antes de decidir. O <a href="https://github.com/akitaonrails/ai-jail/blob/master/docs/sandbox-alternatives.md"target="_blank" rel="noopener">documento completo de análise</a> tá no repositório, mas o resumo é:</p>
<p>Bubblewrap (bwrap) é o sandbox que o Flatpak usa pra isolar todo app desktop. ~50KB de binário, ~4000 linhas de C, mantido pelo time do GNOME. Roda sem root usando <code>CLONE_NEWUSER</code> pra criar namespaces sem privilégio elevado. Tá empacotado em toda distro Linux relevante e testado em escala por milhões de instalações Flatpak.</p>
<p>Considerei e descartei as alternativas. Firejail requer setuid root, e confiar em setuid pra proteger contra agentes que já rodam no seu sistema é contraditório. nsjail e minijail são projetados pra ambientes de produção (Google os usa internamente), complexos demais pra workstation de dev. systemd-nspawn requer root e é pra containers de sistema, não pra isolar um processo.</p>
<p>Landlock é um caso diferente. Ele não substitui bubblewrap — não tem nada a ver com namespaces ou mount isolation. Mas complementa. Landlock é um LSM (Linux Security Module) que controla acesso no nível do VFS, independente dos mount namespaces. Isso fecha vetores que o bwrap sozinho não cobre: rotas de escape pelo <code>/proc</code>, tricks com symlinks dentro de mounts permitidos, e serve como seguro contra bugs no próprio mecanismo de namespaces. A partir da v0.4.0, o ai-jail aplica Landlock automaticamente em kernels 5.13+ como defense-in-depth. Usa ABI V3 (Linux 6.2+) com degradação graceful pra V1 em kernels mais antigos, e vira no-op silencioso se o kernel não suportar. Se causar problema com alguma ferramenta específica, <code>--no-landlock</code> desativa.</p>
<p>Bubblewrap acerta no ponto exato: isolamento real sem root, em toda distro, e simples o suficiente pra wrappear num binário de 880KB.</p>
<h2>O Que o Sandbox Faz no Linux<span class="hx:absolute hx:-mt-20" id="o-que-o-sandbox-faz-no-linux"></span>
    <a href="#o-que-o-sandbox-faz-no-linux" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando você roda <code>ai-jail claude</code>, o seguinte acontece:</p>
<p>O agente roda em namespaces isolados de PID, UTS e IPC, com hostname <code>ai-sandbox</code>, e morre automaticamente se o pai morrer (<code>--die-with-parent</code>).</p>
<p>O filesystem é montado numa sequência específica (bubblewrap é order-dependent). <code>/usr</code>, <code>/etc</code>, <code>/opt</code>, <code>/sys</code> entram read-only pras ferramentas do sistema. <code>/dev</code> e <code>/proc</code> montados pra acesso a devices e processos. <code>/tmp</code> e <code>/run</code> como tmpfs frescos. GPUs auto-detectadas (<code>/dev/nvidia*</code>, <code>/dev/dri</code>). Docker socket, X11/Wayland, <code>/dev/shm</code>, tudo auto-detectado e montado se existir.</p>
<p>A parte mais importante é o tratamento do home directory. <code>$HOME</code> é montado como tmpfs vazio. Depois, seletivamente, dotfiles são layered por cima. <code>.gnupg</code>, <code>.aws</code>, <code>.ssh</code>, <code>.mozilla</code>, <code>.sparrow</code> nunca são montados (dados sensíveis). <code>.claude</code>, <code>.crush</code>, <code>.codex</code>, <code>.aider</code>, <code>.config</code>, <code>.cargo</code>, <code>.cache</code>, <code>.docker</code> entram como read-write porque os agentes precisam escrever aqui. Todo o resto entra read-only. Dentro de <code>~/.config</code>, subdiretórios de browser são escondidos atrás de tmpfs: <code>BraveSoftware</code>, <code>Bitwarden</code>. Mesma coisa em <code>~/.cache</code>: <code>BraveSoftware</code>, <code>chromium</code>, <code>spotify</code>, <code>nvidia</code>. O agente não consegue nem ver que esses diretórios existem.</p>
<p>O diretório corrente do projeto é o único lugar com permissão de escrita (além dos dotdirs de ferramentas). O agente modifica o código, mas não toca no resto.</p>
<h2>macOS: sandbox-exec<span class="hx:absolute hx:-mt-20" id="macos-sandbox-exec"></span>
    <a href="#macos-sandbox-exec" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No macOS, o backend é o <code>sandbox-exec</code> com profiles SBPL (Sandbox Profile Language). É uma API legada da Apple, depreciada oficialmente mas sem substituto público. Funciona hoje, mas a Apple pode remover no futuro.</p>
<p>O ai-jail gera um profile SBPL em runtime que replica a mesma lógica do Linux:</p>
<ul>
<li>Default deny em tudo</li>
<li>Permite operações de processo (exec, fork, signal)</li>
<li>Permite rede (exceto em lockdown)</li>
<li>Permite leitura global, nega paths sensíveis (<code>.gnupg</code>, <code>.aws</code>, <code>.ssh</code>, <code>~/Library/Keychains</code>, <code>~/Library/Mail</code>)</li>
<li>Permite escrita só em diretório do projeto, dotfiles de ferramentas, e <code>/tmp</code></li>
</ul>
<p>As limitações são reais. GPU (Metal) e Display (Cocoa) são system-level no macOS, sandbox-exec não consegue restringir. As flags <code>--no-gpu</code> e <code>--no-display</code> simplesmente não têm efeito no macOS. A paridade entre plataformas é aproximada, não exata.</p>
<p>Mesmo com essas limitações, é melhor do que rodar o agente completamente aberto. O sandbox-exec protege contra acesso a filesystem sensível e, em lockdown, remove permissões de escrita e rede.</p>
<h2>Windows: Não Suportado (E Provavelmente Nunca Será)<span class="hx:absolute hx:-mt-20" id="windows-não-suportado-e-provavelmente-nunca-será"></span>
    <a href="#windows-n%c3%a3o-suportado-e-provavelmente-nunca-ser%c3%a1" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Não é falta de vontade, é falta de primitivas. Windows não tem equivalente userspace a Linux namespaces. Não tem mount API como bubblewrap. AppContainers existem mas usam um modelo de segurança completamente diferente, requerem privilégios de admin, e mapear a funcionalidade do bwrap pra AppContainers seria efetivamente escrever outro projeto do zero.</p>
<p>A solução pra Windows é WSL 2:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># No WSL 2 (kernel Linux real)</span>
</span></span><span class="line"><span class="cl">sudo apt install bubblewrap
</span></span><span class="line"><span class="cl">cargo install ai-jail
</span></span><span class="line"><span class="cl"><span class="c1"># ou: mise use -g ubi:akitaonrails/ai-jail</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /mnt/c/Users/voce/Projects/meu-app
</span></span><span class="line"><span class="cl">ai-jail claude</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>WSL 2 roda um kernel Linux de verdade. Bubblewrap funciona normalmente. Os arquivos do Windows ficam acessíveis em <code>/mnt/c/</code>. Performance de I/O é mais lenta pelo mount de 9p, mas funciona. Pra projetos grandes, clonar dentro de <code>~/Projects/</code> no lado Linux melhora a performance consideravelmente.</p>
<h2>Lockdown Mode<span class="hx:absolute hx:-mt-20" id="lockdown-mode"></span>
    <a href="#lockdown-mode" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Pra workloads que você realmente não confia, existe <code>--lockdown</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail --lockdown bash</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lockdown faz tudo que o modo normal faz, mas vai além. O projeto é montado read-only (não read-write). GPU, Docker, Display e mise são desabilitados. Flags <code>--rw-map</code> e <code>--map</code> são ignoradas. <code>$HOME</code> vira tmpfs puro, sem dotfiles do host. No Linux, a rede é cortada com <code>--unshare-net</code> e o ambiente é limpo com <code>--clearenv</code>. No macOS, as variáveis de ambiente são limpas e as regras de escrita e rede são removidas do SBPL.</p>
<p>É o sandbox mais restritivo que dá pra fazer sem usar uma VM. Útil pra auditar código de terceiros ou rodar agentes em projetos que você não conhece.</p>
<h2>Bootstrap: Configuração Automática de Permissões<span class="hx:absolute hx:-mt-20" id="bootstrap-configuração-automática-de-permissões"></span>
    <a href="#bootstrap-configura%c3%a7%c3%a3o-autom%c3%a1tica-de-permiss%c3%b5es" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><code>ai-jail --bootstrap</code> gera configurações de permissão pras ferramentas que você usa:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail --bootstrap</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pro <strong>Claude Code</strong>, gera <code>~/.claude/settings.json</code> com listas de allow/deny/ask. Permite git status, diff, log, ls, grep, cargo, npm, python, docker compose. Bloqueia rm -rf, sudo, chmod 777, git push &ndash;force. Pergunta antes de git push, rm, docker run.</p>
<p>Pro <strong>Codex</strong>, gera <code>~/.codex/config.toml</code> com <code>approval_policy = &quot;on-request&quot;</code>.</p>
<p>Pro <strong>OpenCode</strong>, gera <code>~/.config/opencode/opencode.json</code> com permissões de bash, edit, write.</p>
<p>Antes de sobrescrever qualquer arquivo, faz backup automático (<code>settings.json.bak</code>). E rejeita operações se o alvo for um symlink, pra evitar path traversal.</p>
<p>É exatamente o conteúdo que eu coloquei manualmente no <a href="/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/">post anterior</a>, mas automatizado e testado.</p>
<h2>Mas o Claude Code Já Tem Sandbox Próprio<span class="hx:absolute hx:-mt-20" id="mas-o-claude-code-já-tem-sandbox-próprio"></span>
    <a href="#mas-o-claude-code-j%c3%a1-tem-sandbox-pr%c3%b3prio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem. Desde outubro de 2025, o Claude Code oferece um sandbox runtime via o comando <code>/sandbox</code>. E adivinhe o que ele usa por baixo? Bubblewrap no Linux e sandbox-exec no macOS. O mesmo stack.</p>
<p>Mas as diferenças importam.</p>
<p>ai-jail é agnóstico de ferramenta. Funciona com Claude, Codex, OpenCode, Crush, e qualquer comando. O sandbox do Claude só protege o Claude. Se amanhã você trocar de agente, o ai-jail continua funcionando igual.</p>
<p>O ponto que mais me incomoda é o escape hatch. Quando um comando falha por restrição de sandbox, o Claude pode tentar novamente com <code>dangerouslyDisableSandbox</code>, caindo de volta no fluxo normal de permissões. Dá pra desabilitar isso (<code>&quot;allowUnsandboxedCommands&quot;: false</code>), mas é opt-out, não opt-in. No ai-jail, não existe escape hatch. O processo roda dentro do bwrap ou do sandbox-exec, ponto. Não tem como o agente decidir sozinho sair do sandbox.</p>
<p>Outra diferença prática: o <code>.ai-jail</code> vive no diretório do projeto e pode ser commitado no repositório. Qualquer dev que clona o projeto herda a mesma política de sandbox. O sandbox do Claude depende de <code>settings.json</code> global.</p>
<p>Quando roda dentro de Docker, o sandbox do Claude cai pra um modo <code>enableWeakerNestedSandbox</code> que, nas palavras da própria documentação, <em>&ldquo;considerably weakens security&rdquo;</em>. O ai-jail não foi projetado pra rodar dentro de Docker (ele roda diretamente na workstation do dev), então esse problema não existe.</p>
<p>Sobre rede: o sandbox do Claude roteia tráfego por um proxy e permite/bloqueia por domínio. ai-jail no modo normal herda a rede do host; no lockdown, corta a rede inteira com <code>--unshare-net</code>. A abordagem do Claude é mais granular; a do ai-jail é mais simples e mais difícil de contornar.</p>
<p>Os dois não são mutuamente exclusivos. Dá pra rodar o sandbox do Claude dentro do ai-jail. O ai-jail cuida do isolamento de filesystem; o sandbox do Claude adiciona filtragem de rede por domínio. Layers de segurança se somam.</p>
<h2>Por Que Não Usar &ndash;dangerously-skip-permissions Sem Jail<span class="hx:absolute hx:-mt-20" id="por-que-não-usar-dangerously-skip-permissions-sem-jail"></span>
    <a href="#por-que-n%c3%a3o-usar-dangerously-skip-permissions-sem-jail" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Vou ser direto: se você roda Claude Code com <code>--dangerously-skip-permissions</code> sem nenhum sandbox, tá confiando cegamente que a LLM nunca vai executar algo destrutivo. E tá confiando que nenhuma dependência do seu projeto foi comprometida num supply-chain attack.</p>
<p>Toda flag <code>--dangerously</code> tem esse nome por um motivo. O Claude Code é explícito: esse modo existe pra CI/CD e automação em ambientes já isolados (containers, VMs descartáveis). Não pra sua workstation pessoal com <code>~/.aws/credentials</code>, <code>~/.gnupg/</code>, SSH keys, e o banco de senhas do seu browser.</p>
<p>Com ai-jail, o agente tem autonomia total dentro do sandbox. Faz o que quiser no diretório do projeto, usa as ferramentas que precisa, e não consegue acessar nada fora do que foi explicitamente permitido.</p>
<h2>ai-jail + Git: A Rede de Segurança que Você Já Tem<span class="hx:absolute hx:-mt-20" id="ai-jail--git-a-rede-de-segurança-que-você-já-tem"></span>
    <a href="#ai-jail--git-a-rede-de-seguran%c3%a7a-que-voc%c3%aa-j%c3%a1-tem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tem uma coisa que eu não mencionei ainda e que muda o cálculo de risco: se o seu projeto tá num repositório Git, com remote no GitHub/GitLab, e o agente não tem permissão de <code>git push</code>, o dano que ele consegue causar é limitado ao diretório local.</p>
<p>Pense assim. O pior cenário dentro do ai-jail é o agente corromper todos os arquivos do projeto. Irritante? Sim. Catastrófico? Não. Você faz <code>git checkout .</code> e volta ao último commit. Se ele corromper o <code>.git</code> de alguma forma (improvável, mas possível), você deleta o diretório e clona de novo. O remote não foi tocado.</p>
<p>Por isso o <code>--bootstrap</code> do ai-jail coloca <code>git push</code> na lista de &ldquo;ask&rdquo; (perguntar antes), não na lista de &ldquo;allow&rdquo;. E <code>git push --force</code> vai direto pra &ldquo;deny&rdquo;. O agente pode commitar à vontade localmente, pode criar branches, pode fazer rebase. Nada disso afeta o remote. Quando chega na hora de push, você revisa o que ele fez e decide se vai pro ar.</p>
<p>Essa combinação, sandbox pra filesystem + Git pro código + push manual, já te dá um nível de segurança muito razoável pro dia a dia. O ai-jail protege seus dados pessoais e o sistema. O Git protege seu código. E a decisão de publicar continua sendo sua.</p>
<p>Se quiser ir além, as próximas duas seções cobrem camadas adicionais.</p>
<h2>ai-jail vs Dev Containers<span class="hx:absolute hx:-mt-20" id="ai-jail-vs-dev-containers"></span>
    <a href="#ai-jail-vs-dev-containers" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Desde que escrevi o ai-jail, a pergunta mais frequente é: <em>&ldquo;por que não usar Dev Containers?&rdquo;</em>. A resposta curta é que um não substitui o outro. Resolvem problemas diferentes.</p>
<p>Dev Containers (a spec do <a href="https://containers.dev"target="_blank" rel="noopener">containers.dev</a>) define um ambiente de desenvolvimento completo num <code>devcontainer.json</code>. Você especifica imagem base, ferramentas, extensões do VS Code, variáveis de ambiente, e o editor monta tudo pra você num container Docker. O Docker também lançou recentemente os Docker Sandboxes, que vão além e rodam cada agente numa microVM com Firecracker, com isolamento de hardware.</p>
<p>ai-jail não faz nada disso. Ele não define ambiente. Não instala ferramentas. Não roda imagem Docker. Ele pega o ambiente que já existe na sua máquina e restringe o que o processo pode acessar.</p>
<p>Na prática, a diferença é:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>Dev Container</th>
          <th>ai-jail</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>O que faz</td>
          <td>Define e provisiona um ambiente isolado completo</td>
          <td>Restringe acesso do processo ao filesystem existente</td>
      </tr>
      <tr>
          <td>Setup</td>
          <td><code>devcontainer.json</code> + Docker</td>
          <td><code>.ai-jail</code> TOML + bubblewrap</td>
      </tr>
      <tr>
          <td>Startup</td>
          <td>Segundos (pull de imagem, build de container)</td>
          <td>Milissegundos (fork + exec do bwrap)</td>
      </tr>
      <tr>
          <td>Ferramentas</td>
          <td>As que você coloca na imagem</td>
          <td>As que já estão instaladas na sua máquina</td>
      </tr>
      <tr>
          <td>GPU</td>
          <td>Requer configuração de NVIDIA Container Toolkit</td>
          <td>Auto-detecta <code>/dev/nvidia*</code> e <code>/dev/dri</code></td>
      </tr>
      <tr>
          <td>Daemon</td>
          <td>Requer Docker daemon rodando</td>
          <td>Nada além do bwrap</td>
      </tr>
      <tr>
          <td>Reprodutibilidade</td>
          <td>Alta (imagem fixa)</td>
          <td>Depende do que tá instalado no host</td>
      </tr>
      <tr>
          <td>Isolamento de rede</td>
          <td>Docker Sandboxes: firewall por domínio</td>
          <td>Lockdown: corta tudo com <code>--unshare-net</code></td>
      </tr>
  </tbody>
</table>
<p>Dev Container faz mais sentido quando você precisa que todo o time tenha exatamente o mesmo ambiente, ou quando o projeto tem dependências que ninguém quer instalar no host, ou pra rodar agentes não-interativos em CI/CD. Docker Sandboxes com microVM são o isolamento mais forte que existe fora de uma VM dedicada.</p>
<p>ai-jail faz mais sentido quando você já tem o ambiente configurado e quer startup instantâneo sem Docker daemon. Ou quando usa ferramentas que são chatas de rodar dentro de container (CUDA, Wayland, mise). Ou simplesmente quando quer a mesma proteção pra qualquer agente, não só os que têm integração com devcontainer.</p>
<p>E dá pra combinar. Eu conheço gente que roda ai-jail dentro de um Dev Container pra ter reprodutibilidade de ambiente + restrição de filesystem. Layers de segurança se somam.</p>
<h2>Sistemas Operacionais Imutáveis: A Última Camada<span class="hx:absolute hx:-mt-20" id="sistemas-operacionais-imutáveis-a-última-camada"></span>
    <a href="#sistemas-operacionais-imut%c3%a1veis-a-%c3%baltima-camada" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você quer levar a segurança a sério, a terceira camada é o sistema operacional em si.</p>
<p>Sistemas imutáveis como <a href="https://fedoraproject.org/atomic-desktops/silverblue/"target="_blank" rel="noopener">Fedora Silverblue</a>, <a href="https://nixos.org/"target="_blank" rel="noopener">NixOS</a>, e <a href="https://aeondesktop.github.io/"target="_blank" rel="noopener">openSUSE Aeon</a> têm o filesystem raiz read-only. O sistema base não pode ser modificado por nenhum processo, nem com root. Atualizações são atômicas: ou aplicam completamente ou não aplicam. E se algo der errado, você faz rollback pra imagem anterior em um reboot.</p>
<p>Na prática, isso significa que mesmo se um agente de IA escapasse do sandbox (exploiting uma vulnerabilidade no kernel, por exemplo), ele não conseguiria modificar o sistema de forma persistente. No próximo reboot, o sistema volta ao estado declarado. No NixOS, o sistema inteiro é definido por um arquivo de configuração (<code>configuration.nix</code>). No Silverblue, o base é uma imagem OSTree que recebe updates atômicos via <code>rpm-ostree</code>.</p>
<p>Pra desenvolvedores, a pegada é: suas ferramentas de dev rodam em containers (Toolbox/Distrobox no Silverblue, <code>nix-shell</code> no NixOS). O sistema base fica intocado. Apps desktop vêm via Flatpak, que já roda em sandbox. O resultado é que a superfície de ataque do host é mínima.</p>
<p>Fedora Silverblue é provavelmente a entrada mais acessível. Já é Fedora por baixo, com GNOME, funciona com hardware que Fedora suporta, e o Toolbox te dá um Fedora Server containerizado onde você instala o que quiser sem tocar no host. NixOS é mais poderoso (reprodutibilidade total, rollback declarativo), mas a curva de aprendizado é real.</p>
<p>A combinação completa fica assim: o OS imutável cuida do sistema (filesystem read-only, updates atômicos, rollback em um reboot). O ai-jail cuida da sessão (namespace isolado, home efêmero, dados sensíveis invisíveis). E o Git cuida do código (remote intocado enquanto o agente não tiver push).</p>
<p>Nenhuma dessas camadas é perfeita sozinha. Mas o ataque que fura as três ao mesmo tempo — escapar do namespace, persistir num filesystem read-only, e corromper um remote Git — é um cenário que eu ficaria confortável chamando de improvável.</p>
<p>O melhor é que nenhuma exige mudar como você trabalha. O ai-jail é um comando antes do seu agente. Git você já usa. E um OS imutável é uma instalação, não uma mudança de workflow.</p>
<h2>Detalhes Técnicos (Pra Quem Se Importa)<span class="hx:absolute hx:-mt-20" id="detalhes-técnicos-pra-quem-se-importa"></span>
    <a href="#detalhes-t%c3%a9cnicos-pra-quem-se-importa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Escrito em Rust com 124 testes e 4 dependências: <code>lexopt</code> (parsing de CLI sem clap), <code>serde</code> + <code>toml</code> (config), <code>nix</code> (syscalls Unix). Sem runtime async, sem framework de cores (ANSI direto), binary de ~880KB com LTO e strip.</p>
<p>O signal handling é feito corretamente: SIGINT, SIGTERM, e SIGHUP são forwarded pro processo filho. O handler só chama <code>libc::kill</code>, que é async-signal-safe. O reaping do processo usa <code>waitpid</code> em loop com retry em EINTR.</p>
<p>Arquivos temporários (como o <code>/etc/hosts</code> customizado que o sandbox monta) usam RAII: um <code>SandboxGuard</code> que implementa <code>Drop</code> em Rust. Se o processo pai morrer por qualquer razão, o cleanup acontece.</p>
<p>A compatibilidade de configuração é garantida por política de desenvolvimento: nunca remover campos, nunca renomear, campos novos sempre com <code>#[serde(default)]</code>, campos desconhecidos são silenciosamente ignorados. Testes de regressão pra formatos antigos de <code>.ai-jail</code> garantem que atualizar o binário nunca quebra configs existentes. São 32 testes só de config.</p>
<h2>Roadmap<span class="hx:absolute hx:-mt-20" id="roadmap"></span>
    <a href="#roadmap" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O que falta:</p>
<ul>
<li>Mais backends de ferramentas no bootstrap: Aider, Cursor, Windsurf. Conforme mais agentes padronizarem arquivos de configuração de permissão.</li>
<li>Profile sharing pra monorepos, pra não ter que configurar cada serviço separadamente.</li>
</ul>
<h2>Instalação e Primeiro Uso (Resumo Rápido)<span class="hx:absolute hx:-mt-20" id="instalação-e-primeiro-uso-resumo-rápido"></span>
    <a href="#instala%c3%a7%c3%a3o-e-primeiro-uso-resumo-r%c3%a1pido" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. Instalar</span>
</span></span><span class="line"><span class="cl">brew tap akitaonrails/tap <span class="o">&amp;&amp;</span> brew install ai-jail
</span></span><span class="line"><span class="cl"><span class="c1"># ou: cargo install ai-jail</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. No Linux, instalar bubblewrap</span>
</span></span><span class="line"><span class="cl">sudo pacman -S bubblewrap  <span class="c1"># Arch</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sudo apt install bubblewrap  # Debian/Ubuntu</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Entrar no projeto e rodar</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ~/Projects/meu-app
</span></span><span class="line"><span class="cl">ai-jail claude
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 4. (Opcional) Gerar configs de permissão</span>
</span></span><span class="line"><span class="cl">ai-jail --bootstrap
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 5. (Opcional) Ver o que o sandbox faz</span>
</span></span><span class="line"><span class="cl">ai-jail --dry-run --verbose claude</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>.ai-jail</code> criado pode ser commitado no seu repo. A partir daí, qualquer dev que clonar o projeto roda <code>ai-jail claude</code> e tem o mesmo sandbox.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O shell script de janeiro resolveu o problema. O ai-jail resolve o problema direito. Config por projeto, suporte a macOS, lockdown mode, bootstrap de permissões, dry-run pra auditar, e um binário de 880KB que instala em 10 segundos.</p>
<p>Se você usa agentes de IA pra programar, rode eles num sandbox. A boa vontade da LLM não é garantia de nada, e supply-chain attacks não escolhem vítima. Isolamento de processo é a barreira que funciona.</p>
<p>O projeto é GPL-3.0 e tá no GitHub: <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">github.com/akitaonrails/ai-jail</a></p>
<p>Issues e PRs são bem-vindos.</p>
]]></content:encoded><category>ai-jail</category><category>agentes-de-codigo</category><category>seguranca</category><category>rust</category></item><item><title>AI Agents: Garantindo a Proteção do seu Sistema</title><link>https://akitaonrails.github.io/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/01/10/ai-agents-garantindo-a-protecao-do-seu-sistema/</guid><pubDate>Sat, 10 Jan 2026 11:50:36 -0300</pubDate><description>&lt;p&gt;Hoje em dia temos dezenas de Agentes de IA pra programação, seja Cursor, Claude Code, Windsurf, Copilot, OpenCode, Crush e mais. Mas tem um problema que já virou até meme: o risco deles danificarem seu sistema ou enviar seus dados privados pra alguém na Internet (comportamento de malware).&lt;/p&gt;
&lt;p&gt;O problema é que pra serem eficientes, essas ferramentas precisam ter acesso ao seu sistema de arquivos pra vasculhar os arquivos do seu projeto, pra analizar o código. E mais: conseguir rodar ferramentas como compiladores, linters, e coisas mundanas como grep, sed, awk, ls, e outros comandos do seu sistema.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hoje em dia temos dezenas de Agentes de IA pra programação, seja Cursor, Claude Code, Windsurf, Copilot, OpenCode, Crush e mais. Mas tem um problema que já virou até meme: o risco deles danificarem seu sistema ou enviar seus dados privados pra alguém na Internet (comportamento de malware).</p>
<p>O problema é que pra serem eficientes, essas ferramentas precisam ter acesso ao seu sistema de arquivos pra vasculhar os arquivos do seu projeto, pra analizar o código. E mais: conseguir rodar ferramentas como compiladores, linters, e coisas mundanas como grep, sed, awk, ls, e outros comandos do seu sistema.</p>
<p>Eventualmente, uma LLM pode mandar executar um <code>rm -Rf /</code> aleatório e apagar coisas no seu sistema, por exemplo. O código em si, se por acidente apagar o que não devia, teoricamente você deveria estar seguro porque seu código já deveria estar, no mínimo, num repositório de Git (GitHub, GitLab, etc), então seria só reverter a mudança. Mas se o agente rodar um <code>rm</code> fora do diretório do seu projeto, pode criar todo tipo de problema.</p>
<p>Não é comum isso acontecer e todas as ferramentas costumam ter checagens pra isso, incluindo perguntar se você quer realmente executar o comando. Ao mesmo tempo, eles também tem a opção de permitir rodar qualquer comando durante a sessão, sem perguntar - porque uma hora começa a ficar tedioso dar &ldquo;ok&rdquo; pra cada comando individual. Todo mundo acaba ficando com preguiça e diz <em>&ldquo;foda-se, roda o que quiser&rdquo;</em>.</p>
<p>E vocês devem estar pensando <em>&ldquo;ah, minha ferramenta X é bem comportada, ele sempre pergunta antes de rodar qualquer coisa, então eu controlo&rdquo;</em>. Não esqueçam: <strong>ATAQUES DE SUPPLY-CHAIN</strong> existem. Toda hora alguma biblioteca open source é invadida com malware e os programas que usam essas libs acabam sendo infectados sem saber. Isso já aconteceu várias vezes, em várias libs de NPM por exemplo. Não tem como garantir que mesmo um software open source seja confiável. Sempre parta do princípio que tudo é inseguro até prova em contrário e trate esses programas dessa forma: com restrição máxima.</p>
<p>Pra garantir que esses agentes realmente não vão fazer nada fora do diretório do projeto, o melhor é criar um <strong>JAIL</strong>. E aqui vai o comando pra isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bwrap --ro-bind /usr /usr <span class="se">\
</span></span></span><span class="line"><span class="cl">      --ro-bind /bin /bin <span class="se">\
</span></span></span><span class="line"><span class="cl">      --ro-bind /lib /lib <span class="se">\
</span></span></span><span class="line"><span class="cl">      --ro-bind /lib64 /lib64 <span class="se">\
</span></span></span><span class="line"><span class="cl">      --dev /dev <span class="se">\
</span></span></span><span class="line"><span class="cl">      --proc /proc <span class="se">\
</span></span></span><span class="line"><span class="cl">      --bind <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span> <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      --chdir <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      --unshare-all <span class="se">\
</span></span></span><span class="line"><span class="cl">      --share-net <span class="se">\
</span></span></span><span class="line"><span class="cl">      bash</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>bwrap</code> é o <strong>Bubblewrap</strong> um dos componentes que permitem a existência de coisas como <strong>Flatpak</strong>. Ele cria um sandbox. O Bash que vai rodar ao executar o comando acima está essencialmente em &ldquo;jail&rdquo;, ou seja, &ldquo;preso&rdquo;. Nele damos acesso somente de leitura a alguns diretórios do sistema caso precise de bibliotecas e ferramentas. Pra isso serve o <code>--ro-bind</code>.</p>
<p>Mas somente o diretório corrente <code>pwd</code> vai ter permissão de escrita. Qualquer comando que rodar dentro desse jail não tem permissão de escrever em nenhum outro lugar o seu sistema. Veja o exemplo na foto abaixo onde eu tento apagar um arquivo na minha <code>$HOME</code>:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260110120225_screenshot-2026-01-10_12-02-14.png" alt="sandbox"  loading="lazy" /></p>
<p>Ele não consegue nem listar os arquivos na minha home. É tudo invisível dentro do sandbox. Essa é uma das vantagens de apps rodando com Flatpak: eles são muito mais isolados do que apps instalados direto.</p>
<p>Dentro desse bash em sandbox podemos agora executar <code>opencode</code>, <code>crush</code>, <code>cursor</code> ou qualquer outro agente e ter certeza que eles não vão fazer nenhuma besteira no seu sistema.</p>
<p>Pra facilitar, recomendo criar um script como <code>~/.local/bin/ai-jail</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ai-jail — bubblewrap sandbox for AI coding agents</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Mounts the project dir read-write, auto-discovers home dotfiles with a</span>
</span></span><span class="line"><span class="cl"><span class="c1"># deny-list for sensitive dirs, and isolates namespaces.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Usage: ai-jail [--map PATH]... COMMAND [ARGS...]</span>
</span></span><span class="line"><span class="cl"><span class="c1">#        ai-jail claude</span>
</span></span><span class="line"><span class="cl"><span class="c1">#        ai-jail bash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">PROJECT_DIR</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">TEMP_HOSTS</span><span class="o">=</span><span class="k">$(</span>mktemp /tmp/bwrap-hosts.XXXXXX<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">trap</span> <span class="s1">&#39;rm -f &#34;$TEMP_HOSTS&#34;&#39;</span> EXIT
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Mise discovery ─────────────────────────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">REAL_MISE_BIN</span><span class="o">=</span><span class="k">$(</span><span class="nb">type</span> -p mise 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Localhost fix (Go resolver needs /etc/hosts) ───────────────</span>
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s1">&#39;127.0.0.1 localhost ai-sandbox\n::1       localhost ai-sandbox\n&#39;</span> &gt; <span class="s2">&#34;</span><span class="nv">$TEMP_HOSTS</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Parse --map / --rw-map arguments ─────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">EXTRA_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> --map <span class="o">||</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">1</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> --rw-map <span class="o">]]</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nv">FLAG</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">MAP_PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$2</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> -e <span class="s2">&#34;</span><span class="nv">$MAP_PATH</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$FLAG</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;--rw-map&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            <span class="nv">EXTRA_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$MAP_PATH</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$MAP_PATH</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="nv">EXTRA_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--ro-bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$MAP_PATH</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$MAP_PATH</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Warning: Path </span><span class="nv">$MAP_PATH</span><span class="s2"> not found, skipping.&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="nb">shift</span> <span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Mise init command ──────────────────────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$REAL_MISE_BIN</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">MISE_INIT</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$REAL_MISE_BIN</span><span class="s2"> trust &amp;&amp; eval \&#34;\$(</span><span class="nv">$REAL_MISE_BIN</span><span class="s2"> activate bash)\&#34; &amp;&amp; eval \&#34;\$(</span><span class="nv">$REAL_MISE_BIN</span><span class="s2"> env)\&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nv">MISE_INIT</span><span class="o">=</span><span class="s2">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Dotfile deny-list (never mounted — sensitive data) ─────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOTDIR_DENY</span><span class="o">=(</span>.gnupg .aws .mozilla .basilisk-dev .sparrow<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Subdirs of ~/.config to hide (tmpfs over rw config mount)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CONFIG_DENY</span><span class="o">=(</span>BraveSoftware Bitwarden<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Subdirs of ~/.cache to hide (sensitive browser/app caches)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CACHE_DENY</span><span class="o">=(</span>BraveSoftware basilisk-dev chromium spotify nvidia mesa_shader_cache<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Dotdirs requiring read-write access</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOTDIR_RW</span><span class="o">=(</span>.claude .crush .codex .aider .config .cargo .cache .docker<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Helper functions ───────────────────────────────────────────</span>
</span></span><span class="line"><span class="cl">is_denied<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">name</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> d in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DOTDIR_DENY</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;</span><span class="nv">$d</span><span class="s2">&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="k">return</span> 0<span class="p">;</span> <span class="k">done</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">is_rw<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">local</span> <span class="nv">name</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> d in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DOTDIR_RW</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span> <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;</span><span class="nv">$d</span><span class="s2">&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="k">return</span> 0<span class="p">;</span> <span class="k">done</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Auto-discover dot-directories in $HOME ─────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Only directories — regular dotfiles (e.g. .claude.json) are NOT mounted.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># The tmpfs $HOME is writable so tools can create dotfiles as needed.</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOTFILE_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> entry in <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">&#34;</span>/.*<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$entry</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">    <span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="nv">$entry</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">[[</span> <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;.&#34;</span> <span class="o">||</span> <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;..&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">    is_denied <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> is_rw <span class="s2">&#34;</span><span class="nv">$name</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nv">DOTFILE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$entry</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/</span><span class="nv">$name</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="nv">DOTFILE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--ro-bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$entry</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/</span><span class="nv">$name</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Explicit dotfile mounts (regular files) ────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.gitconfig&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DOTFILE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--ro-bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.gitconfig&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.gitconfig&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.claude.json&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DOTFILE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.claude.json&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.claude.json&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Hide sensitive subdirs inside ~/.config (after rw mount) ───</span>
</span></span><span class="line"><span class="cl"><span class="nv">CONFIG_HIDE_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> denied in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">CONFIG_DENY</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/</span><span class="nv">$denied</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">CONFIG_HIDE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--tmpfs&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/</span><span class="nv">$denied</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Hide sensitive subdirs inside ~/.cache (after rw mount) ──</span>
</span></span><span class="line"><span class="cl"><span class="nv">CACHE_HIDE_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> denied in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">CACHE_DENY</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/</span><span class="nv">$denied</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">CACHE_HIDE_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--tmpfs&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/</span><span class="nv">$denied</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Override ~/.local subdirs as rw (parent .local is ro) ──────</span>
</span></span><span class="line"><span class="cl"><span class="nv">LOCAL_OVERRIDES</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/state&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">LOCAL_OVERRIDES</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/state&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/state&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> rw_share in zoxide crush opencode atuin mise yarn flutter kotlin NuGet pipx ruby-advisory-db uv<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share/</span><span class="nv">$rw_share</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">LOCAL_OVERRIDES</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share/</span><span class="nv">$rw_share</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/share/</span><span class="nv">$rw_share</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── GPU device mounts (NVIDIA + DRM) ─────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">GPU_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> dev in /dev/nvidia* /dev/dri<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -e <span class="s2">&#34;</span><span class="nv">$dev</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">GPU_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--dev-bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$dev</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$dev</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Docker socket ────────────────────────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOCKER_MOUNT</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -S /var/run/docker.sock <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DOCKER_MOUNT</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;/var/run/docker.sock&#34;</span> <span class="s2">&#34;/var/run/docker.sock&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Shared memory (Chromium needs large /dev/shm) ───────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">SHM_MOUNT</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d /dev/shm <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">SHM_MOUNT</span><span class="o">+=(</span><span class="s2">&#34;--dev-bind&#34;</span> <span class="s2">&#34;/dev/shm&#34;</span> <span class="s2">&#34;/dev/shm&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Display passthrough (X11 + Wayland) ─────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nv">DISPLAY_MOUNTS</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl"><span class="nv">DISPLAY_ENV</span><span class="o">=()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># X11 / XWayland socket</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -d /tmp/.X11-unix <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DISPLAY_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;/tmp/.X11-unix&#34;</span> <span class="s2">&#34;/tmp/.X11-unix&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DISPLAY</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DISPLAY_ENV</span><span class="o">+=(</span><span class="s2">&#34;--setenv&#34;</span> <span class="s2">&#34;DISPLAY&#34;</span> <span class="s2">&#34;</span><span class="nv">$DISPLAY</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">XAUTHORITY</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DISPLAY_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--ro-bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$XAUTHORITY</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$XAUTHORITY</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DISPLAY_ENV</span><span class="o">+=(</span><span class="s2">&#34;--setenv&#34;</span> <span class="s2">&#34;XAUTHORITY&#34;</span> <span class="s2">&#34;</span><span class="nv">$XAUTHORITY</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Wayland socket (lives in XDG_RUNTIME_DIR, typically /run/user/UID)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">XDG_RUNTIME_DIR</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> -d <span class="s2">&#34;</span><span class="nv">$XDG_RUNTIME_DIR</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DISPLAY_MOUNTS</span><span class="o">+=(</span><span class="s2">&#34;--bind&#34;</span> <span class="s2">&#34;</span><span class="nv">$XDG_RUNTIME_DIR</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$XDG_RUNTIME_DIR</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="nv">DISPLAY_ENV</span><span class="o">+=(</span><span class="s2">&#34;--setenv&#34;</span> <span class="s2">&#34;XDG_RUNTIME_DIR&#34;</span> <span class="s2">&#34;</span><span class="nv">$XDG_RUNTIME_DIR</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -n <span class="s2">&#34;</span><span class="si">${</span><span class="nv">WAYLAND_DISPLAY</span><span class="k">:-</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nv">DISPLAY_ENV</span><span class="o">+=(</span><span class="s2">&#34;--setenv&#34;</span> <span class="s2">&#34;WAYLAND_DISPLAY&#34;</span> <span class="s2">&#34;</span><span class="nv">$WAYLAND_DISPLAY</span><span class="s2">&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ── Assemble and launch ───────────────────────────────────────</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Jail Active: </span><span class="nv">$PROJECT_DIR</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">bwrap <span class="se">\
</span></span></span><span class="line"><span class="cl">  --ro-bind /usr /usr <span class="se">\
</span></span></span><span class="line"><span class="cl">  --symlink usr/bin /bin <span class="se">\
</span></span></span><span class="line"><span class="cl">  --symlink usr/lib /lib <span class="se">\
</span></span></span><span class="line"><span class="cl">  --symlink usr/lib /lib64 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --ro-bind /etc /etc <span class="se">\
</span></span></span><span class="line"><span class="cl">  --ro-bind <span class="s2">&#34;</span><span class="nv">$TEMP_HOSTS</span><span class="s2">&#34;</span> /etc/hosts <span class="se">\
</span></span></span><span class="line"><span class="cl">  --ro-bind /opt /opt <span class="se">\
</span></span></span><span class="line"><span class="cl">  --ro-bind /sys /sys <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dev /dev <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">GPU_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">SHM_MOUNT</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --proc /proc <span class="se">\
</span></span></span><span class="line"><span class="cl">  --tmpfs /tmp <span class="se">\
</span></span></span><span class="line"><span class="cl">  --tmpfs /run <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DOCKER_MOUNT</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DISPLAY_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --tmpfs <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DOTFILE_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">CONFIG_HIDE_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">CACHE_HIDE_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">LOCAL_OVERRIDES</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">EXTRA_MOUNTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --bind <span class="s2">&#34;</span><span class="nv">$PROJECT_DIR</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$PROJECT_DIR</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --chdir <span class="s2">&#34;</span><span class="nv">$PROJECT_DIR</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --die-with-parent <span class="se">\
</span></span></span><span class="line"><span class="cl">  --unshare-pid <span class="se">\
</span></span></span><span class="line"><span class="cl">  --unshare-uts <span class="se">\
</span></span></span><span class="line"><span class="cl">  --unshare-ipc <span class="se">\
</span></span></span><span class="line"><span class="cl">  --hostname <span class="s2">&#34;ai-sandbox&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;</span><span class="si">${</span><span class="nv">DISPLAY_ENV</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --setenv PS1 <span class="s2">&#34;(jail) \w \$ &#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --setenv _ZO_DOCTOR <span class="m">0</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  bash -c <span class="s2">&#34;</span><span class="nv">$MISE_INIT</span><span class="s2"> &amp;&amp; </span><span class="si">${</span><span class="p">*</span><span class="k">:-</span><span class="nv">bash</span><span class="si">}</span><span class="s2">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esta versão é bem customizada, deem uma boa lida porque tem deny-list pra que só diretórios de que você realmente precisa dentro do Jail estejam disponíveis. Alguns são read-write porque um Claude precisa conseguir escrever em <code>~/.claude</code> ou Codex precisa do <code>~/.codex</code>; vários diretórios nunca devem ir pra dentro do Jail como repositório de senhas de um Brave ou BitWarden. O resto de que pode precisar deve ser tudo read-only. Esse script cuida de tudo isso.</p>
<p>Só fazer <code>chmod +x ~/.local/bin/ai-jail</code>. Agora podemos executar ele direto ou já passar o comando que queremos rodar no sandbox:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>~/.local/bin/ai-jail crush</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Nesta versão ele vai criar um diretório <code>$HOME</code> temporário, caso ele precise gravar algo temporário lá. E também mandei mapear o seu <code>~/.config</code> pros agentes terem acesso às suas configurações. Se precisar de mais coisas como variáveis de ambiente e coisas assim, modifique pra mapear essas coisas também, mas isso é o mínimo pra conseguir montar um sandbox seguro.</p>
<p>Tome muito cuidado com programas que rodam comandos aleatórios no seu sistema. Nada é 100% seguro, especialmente uma LLM que pode halucinar a qualquer momento e decidir apagar coisas, ou ferramentas suspeitas que você baixou sem ter certeza da origem e pode ser um malware disfarçado. Sempre rode coisas realmente suspeitas somente numa VM ou, no mínimo, dentro de um jail como esse.</p>
<p>Isso não serve só pra agentes de IA, lógico. Toda vez que for rodar qualquer comando que tem dúvidas ou ache suspeito, rode dentro desse jail.</p>
<p>De bônus, note que eu também mapeio o <a href="https://akitaonrails.com/2025/09/07/omarchy-2-0-mise-pra-organizar-ambientes-de-desenvolvimento/"target="_blank" rel="noopener">Mise</a> pra dentro do container. Se você usa Omarchy, deve estar usando Mise também.</p>
<p>Especificamente pra Claude Code, que eu mais uso, prefiro não deixar tudo autorizado no modo automático <code>--allow-dangerously-skip-permissions</code>, mas também não gosto de ter que ficar autorizando coisa besta como cada domínio que ele quer pesquisar na web (só pesquisar não tem problema nenhum) ou qualquer comando que só lista coisas como <code>ls</code> ou <code>grep</code> então dá pra melhorar configurando direto em <code>~/.claude/settings.json</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;permissions&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allow&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git add *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git commit *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git status *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git diff *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git log *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git branch *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git checkout *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git switch *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git stash *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git show *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git fetch *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git pull *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git merge *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git tag *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git remote -v*)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git rev-parse *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git ls-files *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(ls *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(find *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(grep *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(wc *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(cat *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(head *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(tail *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(file *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(which *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(echo *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(pwd)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(cd *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(ps *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(env *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(printenv *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(mkdir *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(cp *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(mv *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(touch *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(npm *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(npx *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(yarn *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(pnpm *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(bun *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(node *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(ruby *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(bundle *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rails *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rake *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(go *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(cargo *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rustc *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(python *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(pip *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker compose *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker ps *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker logs *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker images *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(make *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(gh *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(curl *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(jq *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(sed *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(awk *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(sort *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(uniq *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(diff *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(* --version)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(* --help)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;WebSearch&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;WebFetch&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;deny&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rm -rf *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rm -r *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(sudo *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(chmod 777 *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git reset --hard *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git clean *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git push --force *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git push -f *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker rm *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker rmi *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker system prune *)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ask&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git push *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git rebase *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git branch -D *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(git branch -d *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(rm *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(kamal *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker run *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker exec *)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Bash(docker stop *)&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;defaultMode&#34;</span><span class="p">:</span> <span class="s2">&#34;acceptEdits&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;enabledPlugins&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;gopls-lsp@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;typescript-lsp@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pyright-lsp@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;rust-analyzer-lsp@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;github@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pr-review-toolkit@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;frontend-design@claude-plugins-official&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;alwaysThinkingEnabled&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pesquise a documentação (ou pergunte direto pro próprio Claude) sobre mais customizações que dá pra fazer nesse arquivo pra servir melhor pro seu tipo de projeto de fluxo de trabalho.</p>
<p>É basicamente assim que um container de Docker ou Podman funciona também. Eu expliquei isso no meu <a href="https://akitaonrails.com/2023/03/02/akitando-139-entendendo-como-containers-funcionam/"target="_blank" rel="noopener">video sobre Docker</a>. Se não assistiu ainda, recomendo ver.</p>
]]></content:encoded><category>ai-jail</category><category>agentes-de-codigo</category><category>seguranca</category><category>linux</category></item></channel></rss>