<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>FLAMEEE</title><description>Notas de segurança ofensiva de Pedro Augusto de Oliveira (flameee): pentest, red team e segurança de IA/LLM, além de writeups de CTF e bug bounty.</description><link>https://flameee-sec.netlify.app/pt-br/</link><language>pt-BR</language><item><title>O edge te lê antes do payload</title><link>https://flameee-sec.netlify.app/pt-br/posts/tls-fingerprint-what-the-edge-sees/</link><guid isPermaLink="true">https://flameee-sec.netlify.app/pt-br/posts/tls-fingerprint-what-the-edge-sees/</guid><description>Como um servidor decide que você é uma ferramenta antes de ler um único byte do seu request: o ClientHello campo por campo, o JA3 que o Chrome quebrou de propósito em 2023, o JA4 que substituiu ele, e as outras camadas que também contam. Com medições reais desta máquina.</description><pubDate>Tue, 03 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A cena se repete. O request funciona no browser, falha no &lt;code&gt;curl&lt;/code&gt;. Você confere o header, o token, o body, todos idênticos, e conclui que o alvo “detecta ferramenta”. Aí você melhora o User-Agent, adiciona os headers do Chrome, e continua tomando 403.&lt;/p&gt;
&lt;p&gt;O que eu demorei para internalizar é que, nesse ponto, &lt;strong&gt;o servidor provavelmente ainda não leu nada do que você mandou&lt;/strong&gt;. A decisão já foi tomada no handshake, com base em como o seu cliente &lt;em&gt;se apresentou&lt;/em&gt; — antes de existir HTTP, antes de existir header, antes de existir payload. Existe uma camada inteira de identidade que eu passei anos tratando como “detalhe de rede” e que, na prática, decide se o meu teste começa ou não.&lt;/p&gt;
&lt;p&gt;Este post é sobre essa camada. Vou abrir o ClientHello campo por campo, mostrar por que cada biblioteca TLS produz uma assinatura estável, explicar o JA3 e por que ele morreu para browsers em 2023, decodificar o JA4 inteiro, e fechar com as camadas que não são TLS mas contam igual. Tudo com número medido, não com “dizem que”.&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-note&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;NOTE&lt;/p&gt;
&lt;p&gt;As medições vêm de um endpoint local que lê o ClientHello cru e calcula JA3/JA4, alimentado por 10 stacks diferentes (38 conexões). Onde eu cito um fingerprint, ele saiu de um registro. Onde eu cito comportamento, eu digo a versão exata. Nesse assunto, versão é metade da resposta.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 id=&quot;anatomia-de-um-clienthello&quot;&gt;Anatomia de um ClientHello&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#anatomia-de-um-clienthello&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Antes de qualquer fingerprint, o objeto. A norma vigente é a &lt;strong&gt;RFC 9846&lt;/strong&gt; (julho de 2026), que obsoleta a RFC 8446. Sim, o TLS 1.3 foi republicado, e quase todo texto que você vai ler ainda cita a 8446. A estrutura do ClientHello, no entanto, é a mesma:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;struct {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ProtocolVersion legacy_version = 0x0303;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Random random;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    opaque legacy_session_id&amp;#x3C;0..32&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    CipherSuite cipher_suites&amp;#x3C;2..2^16-2&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    opaque legacy_compression_methods&amp;#x3C;1..2^8-1&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Extension extensions&amp;#x3C;8..2^16-1&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} ClientHello;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Seis campos. Vou passar por cada um pensando em uma pergunta só: &lt;strong&gt;isso é sinal ou é ruído?&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Campo&quot;&gt;














































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Campo&lt;/th&gt;&lt;th&gt;Bytes&lt;/th&gt;&lt;th&gt;O que é&lt;/th&gt;&lt;th&gt;Vira fingerprint?&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;legacy_version&lt;/code&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Fixado em &lt;code&gt;0x0303&lt;/code&gt; no TLS 1.3&lt;/td&gt;&lt;td&gt;Quase não: todo mundo manda igual&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;random&lt;/code&gt;&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;Sorteado a cada conexão&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Nunca.&lt;/strong&gt; Se aparecer num hash, o hash está errado&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;legacy_session_id&lt;/code&gt;&lt;/td&gt;&lt;td&gt;0–32&lt;/td&gt;&lt;td&gt;Em modo compatibilidade, 32 bytes aleatórios&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Sim&lt;/strong&gt;: presença e tamanho separam clientes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;cipher_suites&lt;/code&gt;&lt;/td&gt;&lt;td&gt;variável&lt;/td&gt;&lt;td&gt;Lista de suítes, em ordem de preferência&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Muito&lt;/strong&gt;: conjunto &lt;em&gt;e&lt;/em&gt; ordem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;legacy_compression_methods&lt;/code&gt;&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Exatamente &lt;code&gt;00&lt;/code&gt; no TLS 1.3&lt;/td&gt;&lt;td&gt;Hoje é constante&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;extensions&lt;/code&gt;&lt;/td&gt;&lt;td&gt;variável&lt;/td&gt;&lt;td&gt;Conjunto, ordem e conteúdo&lt;/td&gt;&lt;td&gt;&lt;strong&gt;O campo mais rico de todos&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Três coisas aqui merecem atenção, porque são onde o modelo mental costuma quebrar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O &lt;code&gt;legacy_version&lt;/code&gt; é mentira por design.&lt;/strong&gt; O TLS 1.3 não diz a versão ali; ele diz &lt;code&gt;0x0303&lt;/code&gt; (TLS 1.2) e informa a versão real numa extensão, &lt;code&gt;supported_versions&lt;/code&gt;. O motivo é histórico: middlebox velha rejeitava hello com número de versão alto, então o campo legado foi congelado para atravessar equipamento idiota. Consequência direta para fingerprint: &lt;strong&gt;o JA3 lê o campo legado e por isso não distingue 1.2 de 1.3&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O session id de 32 bytes é o “modo compatibilidade”&lt;/strong&gt; (Apêndice E.4 da RFC 9846). Para o handshake 1.3 parecer uma retomada 1.2, o cliente manda um session id não vazio e um &lt;code&gt;change_cipher_spec&lt;/code&gt; falso. Na minha medição, &lt;strong&gt;todas as conexões TLS 1.3 dos 10 stacks mandaram 32 bytes&lt;/strong&gt;, e todos os clientes restritos a TLS 1.2 mandaram vazio, com uma exceção (o Go, que manda 32 bytes nos dois casos). Ou seja: session id vazio num hello 1.3 é um sinal forte de “não sou browser”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O &lt;code&gt;random&lt;/code&gt; não conta.&lt;/strong&gt; Em duas execuções do Python &lt;code&gt;requests&lt;/code&gt;, os bytes brutos do ClientHello são diferentes (&lt;code&gt;0adf02f5c7e64d31…&lt;/code&gt; contra &lt;code&gt;60bebb560e3f784a…&lt;/code&gt;) e o JA3/JA4 são idênticos. Isso é a definição de um fingerprint que funciona: os bytes mudam, a estrutura não.&lt;/p&gt;
&lt;h2 id=&quot;as-extensões-são-a-assinatura&quot;&gt;As extensões são a assinatura&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#as-extensões-são-a-assinatura&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Se você só puder decorar uma tabela neste post, é esta. As extensões são o que separa um cliente do outro, e são identificadas por um número de 16 bits. Estes são os codepoints que aparecem de verdade, conferidos no registro da IANA:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: hex&quot;&gt;
















































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;hex&lt;/th&gt;&lt;th&gt;Nome&lt;/th&gt;&lt;th&gt;O que faz&lt;/th&gt;&lt;th&gt;Onde entra&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0000&lt;/code&gt;&lt;/td&gt;&lt;td&gt;server_name&lt;/td&gt;&lt;td&gt;SNI: o domínio que você quer&lt;/td&gt;&lt;td&gt;&lt;code&gt;d&lt;/code&gt;/&lt;code&gt;i&lt;/code&gt; no JA4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0005&lt;/code&gt;&lt;/td&gt;&lt;td&gt;status_request&lt;/td&gt;&lt;td&gt;pede OCSP stapling&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x000a&lt;/code&gt;&lt;/td&gt;&lt;td&gt;supported_groups&lt;/td&gt;&lt;td&gt;curvas/grupos aceitos&lt;/td&gt;&lt;td&gt;JA3 (campo 4), &lt;strong&gt;fora&lt;/strong&gt; do JA4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x000b&lt;/code&gt;&lt;/td&gt;&lt;td&gt;ec_point_formats&lt;/td&gt;&lt;td&gt;formato de ponto EC&lt;/td&gt;&lt;td&gt;JA3 (campo 5)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x000d&lt;/code&gt;&lt;/td&gt;&lt;td&gt;signature_algorithms&lt;/td&gt;&lt;td&gt;algoritmos de assinatura&lt;/td&gt;&lt;td&gt;JA4, &lt;strong&gt;na ordem&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0010&lt;/code&gt;&lt;/td&gt;&lt;td&gt;ALPN&lt;/td&gt;&lt;td&gt;&lt;code&gt;h2&lt;/code&gt;, &lt;code&gt;http/1.1&lt;/code&gt;…&lt;/td&gt;&lt;td&gt;&lt;code&gt;h2&lt;/code&gt;/&lt;code&gt;h1&lt;/code&gt; no JA4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0012&lt;/code&gt;&lt;/td&gt;&lt;td&gt;signed_certificate_timestamp&lt;/td&gt;&lt;td&gt;SCT (CT)&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0015&lt;/code&gt;&lt;/td&gt;&lt;td&gt;padding&lt;/td&gt;&lt;td&gt;padding do hello&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0017&lt;/code&gt;&lt;/td&gt;&lt;td&gt;extended_master_secret&lt;/td&gt;&lt;td&gt;herança do 1.2&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x001b&lt;/code&gt;&lt;/td&gt;&lt;td&gt;compress_certificate&lt;/td&gt;&lt;td&gt;compressão de cert&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0023&lt;/code&gt;&lt;/td&gt;&lt;td&gt;session_ticket&lt;/td&gt;&lt;td&gt;retomada&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x002b&lt;/code&gt;&lt;/td&gt;&lt;td&gt;supported_versions&lt;/td&gt;&lt;td&gt;&lt;strong&gt;a versão real&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;13&lt;/code&gt;/&lt;code&gt;12&lt;/code&gt; no JA4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x002d&lt;/code&gt;&lt;/td&gt;&lt;td&gt;psk_key_exchange_modes&lt;/td&gt;&lt;td&gt;modos PSK&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x0033&lt;/code&gt;&lt;/td&gt;&lt;td&gt;key_share&lt;/td&gt;&lt;td&gt;material público da troca&lt;/td&gt;&lt;td&gt;contagem, &lt;strong&gt;conteúdo nunca&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0xff01&lt;/code&gt;&lt;/td&gt;&lt;td&gt;renegotiation_info&lt;/td&gt;&lt;td&gt;anti-renegociação&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0xfe0d&lt;/code&gt;&lt;/td&gt;&lt;td&gt;encrypted_client_hello&lt;/td&gt;&lt;td&gt;ECH&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x44cd&lt;/code&gt;&lt;/td&gt;&lt;td&gt;application_settings (ALPS)&lt;/td&gt;&lt;td&gt;settings de aplicação&lt;/td&gt;&lt;td&gt;conjunto&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Duas linhas dessa tabela merecem parágrafo próprio, porque são as que eu mais vi descritas de forma errada.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;0xfe0d&lt;/code&gt; no Chrome é ECH &lt;em&gt;GREASE&lt;/em&gt;, não ECH de verdade.&lt;/strong&gt; O Chrome liga &lt;code&gt;SSL_set_enable_ech_grease(ssl, 1)&lt;/code&gt; no caminho oportunista, e o BoringSSL documenta que isso “sends a GREASE ECH extension when no supported ECHConfig is available”. Como &lt;code&gt;localhost&lt;/code&gt; não tem ECHConfig em DNS, o que sai é um ECH falso: 218 bytes na conexão que eu inspecionei, com payload aleatório, e tamanho que varia. Ou seja: a extensão existe, mas não está escondendo nada, é ruído de extensibilidade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;0x44cd&lt;/code&gt; é ALPS de verdade, e não está registrada.&lt;/strong&gt; O payload que eu capturei é &lt;code&gt;00 03 02 68 32&lt;/code&gt;: um comprimento de lista, um comprimento de protocolo e as letras &lt;code&gt;h2&lt;/code&gt;. É literalmente “minhas settings de aplicação são para h2”. Confirmação cruzada: no Chrome rodando com &lt;code&gt;--disable-http2&lt;/code&gt;, essa extensão &lt;strong&gt;desaparece&lt;/strong&gt; e o hello cai de 17 para 16 extensões. O BoringSSL chama essa família de “ALPS (draft-vvv-tls-alps)” e mantém dois codepoints (antigo e novo, selecionados por &lt;code&gt;alps_use_new_codepoint&lt;/code&gt;), enquanto o draft deixa o valor da IANA como &lt;code&gt;TBD&lt;/code&gt;. Por isso o número que você vê no fio é experimental e some do registro.&lt;/p&gt;
&lt;h2 id=&quot;grease-ruído-de-propósito&quot;&gt;GREASE: ruído de propósito&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#grease-ruído-de-propósito&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A RFC 8701 reserva valores para serem anunciados ao acaso: &lt;code&gt;0x0A0A&lt;/code&gt; até &lt;code&gt;0xFAFA&lt;/code&gt; para extensões, grupos, algoritmos de assinatura e versões; &lt;code&gt;{0x0A,0x0A}&lt;/code&gt; até &lt;code&gt;{0xFA,0xFA}&lt;/code&gt; para cipher suites e ALPN. A ideia é obrigar a outra ponta a ignorar o que não conhece, para que o ecossistema não “enferruje” e trave quando um valor novo aparecer.&lt;/p&gt;
&lt;p&gt;Para fingerprint, GREASE é ruído injetado de propósito, e um anti-fingerprint acidental: o cliente muda o hello sem mudar de software. Na medição, &lt;strong&gt;só o Chrome (e o &lt;code&gt;curl_cffi&lt;/code&gt;, que copia o Chrome) manda GREASE&lt;/strong&gt;: uma cipher, duas extensões, um grupo, uma entrada em &lt;code&gt;supported_versions&lt;/code&gt; e um algoritmo de assinatura, com valores re-aleatorizados a cada conexão. Todos os outros nove stacks não mandam nenhum.&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-important&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;IMPORTANT&lt;/p&gt;
&lt;p&gt;Circula a frase “o JA3 não trata GREASE”. É falsa para a implementação de referência: o &lt;code&gt;ja3.py&lt;/code&gt; da Salesforce tem uma &lt;code&gt;GREASE_TABLE&lt;/code&gt; e filtra ciphers, extensões e curvas; o script Zeek faz o mesmo. O problema real é outro: &lt;strong&gt;implementações de terceiros divergem entre si&lt;/strong&gt;. A Fastly documentou isso com medição: o serviço ja3er descartava a extensão ALPS e devolvia um hash inválido, e num segundo teste uma única curva (x448) aparecia como &lt;code&gt;30&lt;/code&gt; de um lado e &lt;code&gt;1035&lt;/code&gt; do outro. O defeito é de implementação, não do algoritmo.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 id=&quot;tls-12-e-13-não-se-parecem-no-fio&quot;&gt;TLS 1.2 e 1.3 não se parecem no fio&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#tls-12-e-13-não-se-parecem-no-fio&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Vale colocar lado a lado, porque é a diferença mais fácil de perder:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: TLS 1.2&quot;&gt;


































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;TLS 1.2&lt;/th&gt;&lt;th&gt;TLS 1.3&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;versão&lt;/td&gt;&lt;td&gt;&lt;code&gt;legacy_version&lt;/code&gt; e pronto&lt;/td&gt;&lt;td&gt;&lt;code&gt;legacy_version&lt;/code&gt; fixo + &lt;code&gt;supported_versions&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;troca de chave&lt;/td&gt;&lt;td&gt;grupos e formatos de ponto&lt;/td&gt;&lt;td&gt;&lt;code&gt;key_share&lt;/code&gt; com material no hello&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;compressão&lt;/td&gt;&lt;td&gt;lista real&lt;/td&gt;&lt;td&gt;exatamente &lt;code&gt;00&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;session id&lt;/td&gt;&lt;td&gt;retomada real&lt;/td&gt;&lt;td&gt;32 bytes aleatórios (compat)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;resumption&lt;/td&gt;&lt;td&gt;session id / ticket&lt;/td&gt;&lt;td&gt;PSK + &lt;code&gt;psk_key_exchange_modes&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;O efeito no fingerprint é direto e meio cruel: como o JA3 lê a versão legada, &lt;strong&gt;o campo de versão dele é o mesmo&lt;/strong&gt; (&lt;code&gt;771&lt;/code&gt;) em 1.2 e em 1.3. O que separa os dois hellos é o resto da lista, não a versão. O JA4 lê &lt;code&gt;supported_versions&lt;/code&gt; e marca &lt;code&gt;t12&lt;/code&gt; contra &lt;code&gt;t13&lt;/code&gt; na primeira parte. E como 1.3 obriga &lt;code&gt;key_share&lt;/code&gt; e &lt;code&gt;psk_key_exchange_modes&lt;/code&gt;, a contagem de extensões muda junto.&lt;/p&gt;
&lt;h2 id=&quot;cada-stack-escreve-o-próprio-hello&quot;&gt;Cada stack escreve o próprio hello&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#cada-stack-escreve-o-próprio-hello&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Aqui está o insight estrutural que faz tudo o resto fazer sentido: &lt;strong&gt;o ClientHello não é negociado, é escrito&lt;/strong&gt;. Ele é o resultado de uma tabela compilada no binário.&lt;/p&gt;
&lt;p&gt;Na BoringSSL, &lt;code&gt;ssl/extensions.cc&lt;/code&gt; tem uma tabela &lt;code&gt;kExtensions[]&lt;/code&gt; e o builder percorre essa tabela chamando o &lt;code&gt;add_clienthello&lt;/code&gt; de cada entrada. Na prática, a ordem das extensões é uma constante de compilação, exceto quando é embaralhada. E vamos chegar lá. A lista de ciphers também é uma tabela: &lt;code&gt;kCiphers[]&lt;/code&gt;, em &lt;code&gt;ssl_cipher.cc&lt;/code&gt;, com os três suites TLS 1.3 em ordem fixa.&lt;/p&gt;
&lt;p&gt;Só que “tabela compilada” não significa “igual entre stacks”. Medindo, o mesmo pedido por ferramentas diferentes:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Cliente medido&quot;&gt;












































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Cliente medido&lt;/th&gt;&lt;th&gt;Ciphers / extensões (sem GREASE)&lt;/th&gt;&lt;th&gt;JA4&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;curl do macOS 8.7.1 (LibreSSL 3.3.6)&lt;/td&gt;&lt;td&gt;49 / 7&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d4907h2_0d8feac7bc37_7395dae3b2f3&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;curl Homebrew 8.21.0 (OpenSSL 3.6.4)&lt;/td&gt;&lt;td&gt;30 / 12&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d3012h2_1d37bd780c83_882d495ac381&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Python 3.15 (requests e httpx, OpenSSL)&lt;/td&gt;&lt;td&gt;17 / 12&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d1712h1_ab0a1bf427ad_0d46a1bf4a7c&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Go 1.25 &lt;code&gt;net/http&lt;/code&gt;&lt;/td&gt;&lt;td&gt;13 / 12&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d1312h2_f57a46bbacb6_ab7e3b40a677&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Java 21.0.8 &lt;code&gt;java.net.http&lt;/code&gt;&lt;/td&gt;&lt;td&gt;37 / 13&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d3713h2_db35923f8641_7c76daad20ec&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Node 26.8.2 (undici)&lt;/td&gt;&lt;td&gt;52 / 12&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d5212h1_b262b3658495_8e6e362c5eac&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chrome 156&lt;/td&gt;&lt;td&gt;15 / 17&lt;/td&gt;&lt;td&gt;&lt;code&gt;t13d1517h2_8daaf6152771_cb7bf5808d99&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Repare nas duas primeiras linhas. &lt;strong&gt;São os dois &lt;code&gt;curl&lt;/code&gt; da mesma máquina&lt;/strong&gt;: um é o do sistema, o outro é o do Homebrew. Mesmo comando, mesmo nome, fingerprint diferente, porque o do macOS usa LibreSSL e o do Homebrew usa OpenSSL. Se você copiou um tutorial do Linux e rodou no Mac, você está anunciando outra stack.&lt;/p&gt;
&lt;p&gt;A linguagem também não é o sinal. O Python e o Node usam OpenSSL por baixo; o Python dá 17 ciphers e o Node dá 52. O que conta é a biblioteca e a versão dela, não a linguagem.&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-note&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;NOTE&lt;/p&gt;
&lt;p&gt;O Chrome da medição rodou headless via Playwright, no mesmo binário 156.0.8078.80. Headless usa a mesma BoringSSL, mas eu não comparei com uma janela normal. Então leia a linha do Chrome como “este build”, não como “o Chrome”.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 id=&quot;ja3-uma-linha-um-md5-e-um-problema&quot;&gt;JA3: uma linha, um MD5 e um problema&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#ja3-uma-linha-um-md5-e-um-problema&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O JA3 (Salesforce, 2017) é a ideia mais simples possível: pegue cinco campos do hello, junte em texto, hasheie.&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Valores em decimal, &lt;code&gt;,&lt;/code&gt; entre campos, &lt;code&gt;-&lt;/code&gt; dentro do campo, e MD5 no fim. O exemplo do README oficial, que eu recalculei:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  → ada70206e40642a3e4461f35503241d5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Funcionou em 2017 porque era barato de calcular e suficientemente discriminante. E funciona até hoje para o que ele é: um jeito de dizer “esses bytes apareceram nesta ordem”.&lt;/p&gt;
&lt;p&gt;Só que “nesta ordem” é a parte frágil, e a lista de fraquezas é curta e honesta: ordem importa; não há conteúdo de extensão (só os números); 1.2 e 1.3 se confundem; o MD5 é identificador, não autenticador; e a divergência entre implementações que a Fastly mediu. Nenhuma dessas fraquezas é fatal isoladamente. Juntas, elas deixaram o JA3 pronto para ser morto.&lt;/p&gt;
&lt;p&gt;E em 2023 alguém o matou de propósito.&lt;/p&gt;
&lt;h2 id=&quot;2023-o-chrome-quebrou-o-ja3-de-propósito&quot;&gt;2023: o Chrome quebrou o JA3 de propósito&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#2023-o-chrome-quebrou-o-ja3-de-propósito&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;No código do Chromium, &lt;code&gt;net/socket/ssl_client_socket_impl.cc&lt;/code&gt;, existe uma linha incondicional no caminho de conexão TLS:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;CPP&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;SSL_set_permute_extensions&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;ssl_&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;(),&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Do outro lado, na BoringSSL, &lt;code&gt;ssl_setup_extension_permutation()&lt;/code&gt; faz um Fisher-Yates com &lt;code&gt;RAND_bytes&lt;/code&gt; sobre os índices de &lt;code&gt;kExtensions&lt;/code&gt; — e o laço de emissão percorre a permutação em vez da ordem natural. Resultado: &lt;strong&gt;o Chrome embaralha a ordem das extensões do ClientHello a cada conexão&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;O motivo é anti-ossificação, e é um motivo bom: se servidores se acostumam com uma ordem fixa, qualquer mudança futura no Chrome quebra a internet. Embaralhar força o ecossistema a tratar a ordem como irrelevante. A consequência colateral é que o JA3, que depende exatamente dessa ordem, virou ruído.&lt;/p&gt;
&lt;p&gt;A Fastly mediu o estrago na época: a partir de 20 de janeiro de 2023, o JA3 mais comum de Chrome na rede deles caiu; e como o número de permutações é “quase 15 fatorial (cerca de 10¹²)”, na prática cada conexão gera um JA3 único. A Cloudflare foi direta: aquilo tornou “older TLS fingerprints almost useless for identifying the latest version of Chrome”.&lt;/p&gt;
&lt;p&gt;Agora o número que eu medi, porque é aqui que a teoria vira obviedade. &lt;strong&gt;Três conexões novas do mesmo binário do Chrome 156&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;conexão 1  →  JA3 0e250605ce1f2c793bb1a2269dd30e01&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;conexão 2  →  JA3 47e376a38884e22f8a6bb4661daa71a9&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;conexão 3  →  JA3 5c34099f1b500a60c0deacb3f8d31187&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       todas →  JA4 t13d1517h2_8daaf6152771_cb7bf5808d99&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Três JA3 diferentes. Um JA4. Se você construiu uma regra de WAF em cima de JA3 de browser, ela já não vale há três anos.&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-warning&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;WARNING&lt;/p&gt;
&lt;p&gt;E o efeito não para no Chrome: o &lt;code&gt;curl_cffi&lt;/code&gt; com &lt;code&gt;impersonate=chrome&lt;/code&gt; &lt;strong&gt;também permuta&lt;/strong&gt;, porque a fidelidade ao Chrome inclui permutar. Numa execução ele deu &lt;code&gt;892aa6c8…&lt;/code&gt;, na outra &lt;code&gt;be08a432…&lt;/code&gt;, com o mesmo JA4 do Chrome. Ou seja: se você mede “meu impersonator está funcionando” olhando JA3, você vai concluir que está quebrado.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 id=&quot;ja4-como-ler-o-fingerprint-inteiro&quot;&gt;JA4: como ler o fingerprint inteiro&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#ja4-como-ler-o-fingerprint-inteiro&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O JA4 (FoxIO, 2023) resolve isso com duas decisões: ordenar o que é arbitrário, e colocar informação legível na frente do hash. O formato é &lt;code&gt;a_b_c&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;t13d1516h2_8daaf6152771_e5627efa2ab1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Vou decodificar esse exemplo da especificação por inteiro, porque ele é o melhor material de estudo que existe, e porque eu recalculei cada hash.&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Parte&quot;&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Parte&lt;/th&gt;&lt;th&gt;Valor&lt;/th&gt;&lt;th&gt;Significado&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;&lt;td&gt;t&lt;/td&gt;&lt;td&gt;TLS sobre TCP (&lt;code&gt;q&lt;/code&gt; = QUIC, &lt;code&gt;d&lt;/code&gt; = DTLS)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;13&lt;/code&gt;&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;TLS 1.3, lido de &lt;code&gt;supported_versions&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;&lt;td&gt;d&lt;/td&gt;&lt;td&gt;tem SNI (&lt;code&gt;i&lt;/code&gt; = sem SNI, foi para um IP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;15&lt;/code&gt;&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;15 cipher suites, sem contar GREASE&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;16&lt;/code&gt;&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;16 extensões, sem contar GREASE&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;h2&lt;/code&gt;&lt;/td&gt;&lt;td&gt;h2&lt;/td&gt;&lt;td&gt;primeiro e último caractere do primeiro ALPN&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;8daaf6152771&lt;/code&gt;&lt;/td&gt;&lt;td&gt;hash&lt;/td&gt;&lt;td&gt;sha256 das ciphers &lt;strong&gt;ordenadas&lt;/strong&gt;, truncado em 12&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;e5627efa2ab1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;hash&lt;/td&gt;&lt;td&gt;sha256 das extensões &lt;strong&gt;ordenadas&lt;/strong&gt; (menos SNI e ALPN) + sigalgs &lt;strong&gt;na ordem&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;As 15 ciphers, nomeadas pelo registro da IANA: &lt;code&gt;002f&lt;/code&gt; e &lt;code&gt;0035&lt;/code&gt; (RSA CBC), &lt;code&gt;009c&lt;/code&gt; e &lt;code&gt;009d&lt;/code&gt; (RSA GCM), &lt;code&gt;1301&lt;/code&gt;/&lt;code&gt;1302&lt;/code&gt;/&lt;code&gt;1303&lt;/code&gt; (os três suites TLS 1.3), &lt;code&gt;c013&lt;/code&gt;/&lt;code&gt;c014&lt;/code&gt; (ECDHE_RSA CBC), &lt;code&gt;c02b&lt;/code&gt;/&lt;code&gt;c02c&lt;/code&gt; (ECDHE_ECDSA GCM), &lt;code&gt;c02f&lt;/code&gt;/&lt;code&gt;c030&lt;/code&gt; (ECDHE_RSA GCM) e &lt;code&gt;cca8&lt;/code&gt;/&lt;code&gt;cca9&lt;/code&gt; (ECDHE ChaCha20). É a mistura clássica de um cliente moderno: os &lt;code&gt;130x&lt;/code&gt; para 1.3 e uma cesta de 1.2 para conversar com servidor velho.&lt;/p&gt;
&lt;p&gt;As 16 extensões: &lt;code&gt;001b&lt;/code&gt; compress_certificate, &lt;code&gt;0000&lt;/code&gt; SNI, &lt;code&gt;0033&lt;/code&gt; key_share, &lt;code&gt;0010&lt;/code&gt; ALPN, &lt;code&gt;4469&lt;/code&gt; ALPS (o codepoint antigo), &lt;code&gt;0017&lt;/code&gt; extended_master_secret, &lt;code&gt;002d&lt;/code&gt; psk_key_exchange_modes, &lt;code&gt;000d&lt;/code&gt; sigalgs, &lt;code&gt;0005&lt;/code&gt; status_request, &lt;code&gt;0023&lt;/code&gt; session_ticket, &lt;code&gt;0012&lt;/code&gt; SCT, &lt;code&gt;002b&lt;/code&gt; supported_versions, &lt;code&gt;ff01&lt;/code&gt; renegotiation_info, &lt;code&gt;000b&lt;/code&gt; ec_point_formats, &lt;code&gt;000a&lt;/code&gt; supported_groups, &lt;code&gt;0015&lt;/code&gt; padding.&lt;/p&gt;
&lt;p&gt;Recalculando com &lt;code&gt;sha256sum&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sha256(&quot;002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9&quot;)[:12]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  = 8daaf6152771&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sha256(&quot;0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_0403,0804,0401,0503,0805,0501,0806,0601&quot;)[:12]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  = e5627efa2ab1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dois detalhes que valem mais que decorar as regras. Primeiro: SNI e ALPN são &lt;strong&gt;excluídos&lt;/strong&gt; do &lt;code&gt;c&lt;/code&gt; de propósito, porque já estão no &lt;code&gt;a&lt;/code&gt;. Assim, o mesmo cliente dá o mesmo &lt;code&gt;c&lt;/code&gt; indo para um domínio ou para um IP. Segundo: os sigalgs entram &lt;strong&gt;na ordem de aparição, sem ordenar&lt;/strong&gt;, e são colados depois de um &lt;code&gt;_&lt;/code&gt;. Se você ordenar, o hash muda; eu vi essa implementação errada nascer num laboratório e o FoxIO publica o vetor que denuncia o erro (&lt;code&gt;0403,0804,0401,…&lt;/code&gt;, não ordenado).&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-note&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;NOTE&lt;/p&gt;
&lt;p&gt;A própria especificação tem dois exemplos incompatíveis: no topo do documento o fingerprint aparece como &lt;code&gt;…_b186095e22b6&lt;/code&gt;, mas o exemplo trabalhado, com os inputs publicados, dá &lt;code&gt;…_e5627efa2ab1&lt;/code&gt;. Eu recalculei: o segundo é o certo. É um bom lembrete de que até spec precisa ser conferida com &lt;code&gt;sha256sum&lt;/code&gt; antes de virar verdade.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Agora o pulo do gato, comparando o exemplo da especificação com o meu Chrome 156 medido:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;spec       t13d1516h2_8daaf6152771_e5627efa2ab1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Chrome 156 t13d1517h2_8daaf6152771_cb7bf5808d99&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;O &lt;code&gt;b&lt;/code&gt; é &lt;strong&gt;idêntico&lt;/strong&gt;. A lista de ciphers do Chrome não mudou entre esses builds. O que mudou foram as extensões: uma a mais (&lt;code&gt;17&lt;/code&gt; contra &lt;code&gt;16&lt;/code&gt;), e o &lt;code&gt;c&lt;/code&gt; mudou junto. Isso prova que o algoritmo está fazendo o trabalho certo: a parte estável ficou estável, a parte que mudou mudou.&lt;/p&gt;
&lt;h2 id=&quot;ordenar-é-uma-decisão-de-projeto&quot;&gt;Ordenar é uma decisão de projeto&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#ordenar-é-uma-decisão-de-projeto&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O que eu acho mais elegante no JA4 não é o formato, é a escolha de &lt;strong&gt;onde&lt;/strong&gt; ordenar. Eu construí o caso mínimo: o mesmo conjunto de extensões, em duas ordens.&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ordem A: 10-11-13-16-23-35-43-51  →  JA3 692aefc05b6b8a9afca0d6486e3b15e4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ordem B: 51-43-35-23-16-13-11-10  →  JA3 79e887fad94c5bfc974a532532781ec3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                     JA4_c 8ccb6ff8ed2a (igual nos dois)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;./permutacao.svg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Diagrama da permutação: a mesma lista de oito extensões em duas ordens gera dois JA3 diferentes e um único JA4_c&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;figcaption&gt;mesma lista, duas ordens, dois JA3 e um JA4&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Dois JA3, um JA4. E a regra por trás não é “ordem não importa”: é que se ordena o que é &lt;strong&gt;semanticamente irrelevante e ativamente randomizado&lt;/strong&gt;. Extensões TLS se encaixam nas duas coisas. Já a ordem de headers HTTP não é randomizada por ninguém e carrega informação — e o JA4H, o fingerprint de cliente HTTP da mesma família, &lt;strong&gt;não&lt;/strong&gt; ordena os headers. O JA4S, que olha a resposta do servidor, preserva até a ordem original de propósito.&lt;/p&gt;
&lt;p&gt;Três decisões, uma régua só. É isso que separa um formato que envelhece bem de um que precisa ser substituído em cinco anos.&lt;/p&gt;
&lt;h2 id=&quot;o-que-o-ja4-escolhe-não-ver&quot;&gt;O que o JA4 escolhe não ver&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#o-que-o-ja4-escolhe-não-ver&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nenhum fingerprint vê tudo, e vale ser explícito sobre o que fica de fora, porque é aí que ficam as decisões erradas de diagnóstico.&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: O que não entra no JA4&quot;&gt;







































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;O que não entra no JA4&lt;/th&gt;&lt;th&gt;Por quê&lt;/th&gt;&lt;th&gt;Consequência prática&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;random&lt;/code&gt;, session id, bytes do &lt;code&gt;key_share&lt;/code&gt;&lt;/td&gt;&lt;td&gt;são aleatórios&lt;/td&gt;&lt;td&gt;o hash é estável; os bytes nunca são&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;supported_groups&lt;/code&gt;&lt;/td&gt;&lt;td&gt;fora do &lt;code&gt;c&lt;/code&gt; de propósito&lt;/td&gt;&lt;td&gt;dois clientes que só diferem em grupo pós-quântico dão o mesmo JA4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;o nome do SNI&lt;/td&gt;&lt;td&gt;só “tem ou não tem”&lt;/td&gt;&lt;td&gt;o hostname não está no hash&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ordem das extensões&lt;/td&gt;&lt;td&gt;ordenadas&lt;/td&gt;&lt;td&gt;invisível ao JA4, visível ao JA3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ordem das ciphers&lt;/td&gt;&lt;td&gt;ordenadas&lt;/td&gt;&lt;td&gt;você ganha estabilidade, perde a preferência&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ALPN além do primeiro/último char&lt;/td&gt;&lt;td&gt;só 2 caracteres&lt;/td&gt;&lt;td&gt;&lt;code&gt;http/1.1&lt;/code&gt; e &lt;code&gt;http/1.1,h2&lt;/code&gt; ficam idênticos&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Esse último é o mais traiçoeiro, e está nos meus registros: &lt;code&gt;httpx&lt;/code&gt; em modo HTTP/2 oferece &lt;strong&gt;&lt;code&gt;http/1.1, h2&lt;/code&gt;&lt;/strong&gt;, na ordem invertida, e produz exatamente o mesmo JA4 (&lt;code&gt;t13d1712h1_…&lt;/code&gt;) que o &lt;code&gt;requests&lt;/code&gt;, que só oferece &lt;code&gt;http/1.1&lt;/code&gt;. O campo &lt;code&gt;a&lt;/code&gt; pega o primeiro e o último caractere do &lt;strong&gt;primeiro&lt;/strong&gt; valor, então &lt;code&gt;http/1.1&lt;/code&gt; vira &lt;code&gt;h1&lt;/code&gt; e o &lt;code&gt;h2&lt;/code&gt; que veio depois desaparece. Um sensor que só olha JA3/JA4 &lt;strong&gt;não consegue saber que aquele cliente pediu HTTP/2&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;E o pós-quântico merece uma nota: Chrome, Go, Node e OpenSSL 4 oferecem &lt;code&gt;X25519MLKEM768&lt;/code&gt; (&lt;code&gt;0x11ec&lt;/code&gt;, definido na RFC 10024); Java 21 e o curl do macOS não oferecem. Como o JA4 não hasheia grupos nem o conteúdo do &lt;code&gt;key_share&lt;/code&gt; (o Chrome manda um share de 1216 bytes, diferente a cada conexão), &lt;strong&gt;o mesmo cliente antes e depois do PQ mantém o JA4&lt;/strong&gt;. A instabilidade dessa mudança fica toda no JA3, que hasheia &lt;code&gt;supported_groups&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;não-é-só-tls&quot;&gt;Não é só TLS&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#não-é-só-tls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Se você chegou até aqui achando que o problema é TLS, a má notícia é que existem mais três camadas. E uma quarta que não é camada, é histórico.&lt;/p&gt;
&lt;figure&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;./camadas.svg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Diagrama das camadas: TCP/IP, TLS, HTTP/2, HTTP e comportamento, com o que o edge mede em cada uma e se dá para forjar&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;figcaption&gt;cada camada tem um dono · nem todas aceitam disfarce&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;HTTP/2.&lt;/strong&gt; A gramática veio de uma white paper da Akamai de 2017 e é a que todo mundo ainda implementa: o fingerprint é montado com os frames que o cliente manda antes de qualquer request útil. A notação é &lt;code&gt;S[;]|WU|P[,]#|PS[,]&lt;/code&gt;: parâmetros de SETTINGS com seus valores, &lt;strong&gt;na ordem em que aparecem&lt;/strong&gt;; o incremento do WINDOW_UPDATE; as tuplas de prioridade; e a ordem dos pseudo-headers, codificada como &lt;code&gt;m&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;s&lt;/code&gt;. O paper mediu mais de 10 milhões de conexões e, &lt;strong&gt;em 2017&lt;/strong&gt;, publicou a ordem de cada browser: Chrome &lt;code&gt;m,a,s,p&lt;/code&gt;, Firefox &lt;code&gt;m,p,a,s&lt;/code&gt;, Safari &lt;code&gt;m,s,p,a&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Correção de premissa, porque eu mesmo já repeti o erro: &lt;strong&gt;o formato da Akamai não inclui flags de HEADER frame&lt;/strong&gt;. Os três fluxos são SETTINGS, WINDOW_UPDATE e PRIORITY, mais a ordem dos pseudo-headers. Se você vir alguém atribuindo flags de HEADER à Akamai, a fonte não existe.&lt;/p&gt;
&lt;p&gt;Mas 2017 é 2017. Medi os clientes desta máquina agora, com ALPN negociando h2 em todas as conexões para nada degradar em silêncio. Dois deles, no formato acima:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Chrome 156   1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Java 21      1:16384;2:0;3:0;4:16777216;5:16384;6:393216|33488897|0|a,m,p,s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;O &lt;code&gt;PS&lt;/code&gt; do Chrome &lt;strong&gt;continua&lt;/strong&gt; &lt;code&gt;m,a,s,p&lt;/code&gt;: a linha de 2017 sobreviveu. O Go continua &lt;code&gt;a,m,p,s&lt;/code&gt;, batendo com o paper. E o &lt;code&gt;curl&lt;/code&gt; &lt;strong&gt;mudou&lt;/strong&gt;: era &lt;code&gt;m,p,s,a&lt;/code&gt; na 7.54 e virou &lt;code&gt;m,s,a,p&lt;/code&gt; na 8.x. É a razão para colar a versão ao lado de cada fingerprint numa tabela; sem isso, a tabela envelhece sem avisar.&lt;/p&gt;
&lt;p&gt;O campo &lt;code&gt;P&lt;/code&gt; está morto. Em doze conexões, zero frames PRIORITY: &lt;code&gt;P=0&lt;/code&gt; em todas. O &lt;code&gt;P&lt;/code&gt; do paper nasceu das prioridades de stream reservado do Firefox 53; o Chrome de hoje manda o bloco de prioridade (já deprecado) dentro do HEADERS e um header &lt;code&gt;priority&lt;/code&gt; do RFC 9218, e a gramática não codifica nenhum dos dois. Então &lt;code&gt;P=0&lt;/code&gt; significa “não houve frame PRIORITY”, não “não há prioridade”. É uma medida honesta do que um formato de 2017 perde em 2026.&lt;/p&gt;
&lt;p&gt;E a lição da seção de TLS se repete uma camada acima: os dois &lt;code&gt;curl&lt;/code&gt; desta máquina mandam o mesmo &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; (&lt;code&gt;1048510465&lt;/code&gt;) e a mesma ordem de SETTINGS (&lt;code&gt;3;4;2&lt;/code&gt;), mas o Homebrew manda &lt;code&gt;INITIAL_WINDOW_SIZE=65536&lt;/code&gt; e o do macOS manda &lt;code&gt;10485760&lt;/code&gt;. Mesma ferramenta, build diferente, string diferente.&lt;/p&gt;
&lt;p&gt;Isso importa porque um proxy que termina a conexão (e o Burp termina) monta os SETTINGS dele, não os do browser. E o &lt;code&gt;curl_cffi&lt;/code&gt; mostra o limite de tudo isso: ele empata com o Chrome no TLS (JA4 idêntico) e no h2 (string idêntica), e ainda assim é pego uma camada acima: o Chrome 156 da medição manda &lt;code&gt;HeadlessChrome/156&lt;/code&gt; com 17 headers, o &lt;code&gt;curl_cffi&lt;/code&gt; manda &lt;code&gt;Chrome/152&lt;/code&gt; no Windows com 18, incluindo &lt;code&gt;cache-control&lt;/code&gt;. Um impersonator precisa acertar em todas as camadas, e as camadas têm ordem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Headers HTTP.&lt;/strong&gt; Ordem, capitalização e defaults. O JA4H cobre isso hasheando os &lt;em&gt;nomes&lt;/em&gt; na ordem de aparição. A higiene que quase todo mundo esquece é esta: se você forja o User-Agent para “Chrome 131” mas deixa o &lt;code&gt;sec-ch-ua&lt;/code&gt; dizendo outra versão, você não ficou mais parecido com um browser. Ficou &lt;strong&gt;inconsistente&lt;/strong&gt;, o que é pior que o original.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TCP/IP.&lt;/strong&gt; TTL, janela, MSS e a ordem das opções vêm do kernel, não do seu cliente. O p0f faz isso com um SYN desde sempre, e o equivalente moderno é o JA4T, no formato &lt;code&gt;janela_opções_mss_wscale&lt;/code&gt; (o exemplo do FoxIO: &lt;code&gt;64240_2-1-3-1-1-4_1460_8&lt;/code&gt;). É a camada que você não corrige com flag de cliente — e é por isso que mimetizar TLS saindo de um VPS continua contando uma história diferente.&lt;/p&gt;
&lt;h2 id=&quot;fingerprint-é-chave-não-veredito&quot;&gt;Fingerprint é chave, não veredito&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#fingerprint-é-chave-não-veredito&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O erro conceitual mais comum é achar que o edge decide olhando um hash. Ele não decide assim, e a Cloudflare documenta o contrário: junto do JA4 ela calcula o que chama de &lt;strong&gt;JA4 Signals&lt;/strong&gt;: estatística agregada da última hora, global. Os nomes são explícitos: &lt;code&gt;browser_ratio_1h&lt;/code&gt;, &lt;code&gt;h2h3_ratio_1h&lt;/code&gt;, &lt;code&gt;reqs_quantile_1h&lt;/code&gt;, &lt;code&gt;uas_rank_1h&lt;/code&gt;, &lt;code&gt;paths_rank_1h&lt;/code&gt;, &lt;code&gt;cache_ratio_1h&lt;/code&gt;, &lt;code&gt;ips_rank_1h&lt;/code&gt;, entre outros.&lt;/p&gt;
&lt;p&gt;O JA4 é a &lt;strong&gt;chave&lt;/strong&gt;. O que pesa é a distribuição dela. Um JA4 raríssimo que se declara Chrome, com &lt;code&gt;browser_ratio_1h&lt;/code&gt; baixo e volume alto, é um caso perdido, e nenhum ajuste de cipher conserta isso. Pior: essas signals são de analytics, não de regra (“They cannot be used as filter fields in WAF custom rule expressions”), então você não consegue nem simular a decisão olhando só o hash.&lt;/p&gt;
&lt;p&gt;E existe uma confusão que custa horas: &lt;strong&gt;bloqueio e desafio não são a mesma coisa&lt;/strong&gt;. Um desafio volta como HTML, com o header &lt;code&gt;cf-mitigated: challenge&lt;/code&gt;, e existe para rodar JavaScript no seu browser. Um &lt;code&gt;cf_clearance&lt;/code&gt; obtido ali é amarrado ao dispositivo que resolveu, e resolver de um IP e usar de outro invalida o solve. Se o seu caso é esse, o problema não é TLS nenhum, e nenhum impersonator vai resolver.&lt;/p&gt;
&lt;h2 id=&quot;quem-calcula-isso-hoje&quot;&gt;Quem calcula isso hoje&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#quem-calcula-isso-hoje&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Não é nicho de pesquisa. É campo de regra em produção:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Onde&quot;&gt;







































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Onde&lt;/th&gt;&lt;th&gt;O quê&lt;/th&gt;&lt;th&gt;Desde&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Cloudflare&lt;/td&gt;&lt;td&gt;JA4 na Bot Management (Enterprise), campo &lt;code&gt;cf.bot_management.ja4&lt;/code&gt;&lt;/td&gt;&lt;td&gt;JA4+Signals anunciados em ago/2024&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS WAF&lt;/td&gt;&lt;td&gt;JA3&lt;/td&gt;&lt;td&gt;set/2023&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS WAF&lt;/td&gt;&lt;td&gt;JA4, inclusive como chave de rate-based rule&lt;/td&gt;&lt;td&gt;mar/2025&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Google Cloud Armor&lt;/td&gt;&lt;td&gt;&lt;code&gt;origin.tls_ja4_fingerprint&lt;/code&gt; e &lt;code&gt;origin.tls_ja3_fingerprint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;documentado hoje&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Akamai&lt;/td&gt;&lt;td&gt;JA4 (beta, via header injetado)&lt;/td&gt;&lt;td&gt;2026&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fastly&lt;/td&gt;&lt;td&gt;variável VCL &lt;code&gt;tls.client.ja4&lt;/code&gt; e NGWAF&lt;/td&gt;&lt;td&gt;fev/2025&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Nas 38 conexões, os 10 stacks produziram &lt;strong&gt;24 JA4 distintos&lt;/strong&gt;, e três clientes diferentes colapsaram no mesmo (&lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;httpx&lt;/code&gt; em h1 e em h2). Isso resume o estado da arte: o JA4 é bom em dizer “que tipo de software é isso”, e insuficiente para dizer “qual ferramenta exatamente”. Para isso você precisa da próxima camada.&lt;/p&gt;
&lt;h2 id=&quot;o-que-eu-levo-disso&quot;&gt;O que eu levo disso&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#o-que-eu-levo-disso&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Quatro coisas, e nenhuma delas é “use ferramenta X”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O bloqueio acontece antes do payload.&lt;/strong&gt; Se o seu encoding, o seu header e o seu token estão certos e ainda dá 403, a resposta provavelmente foi tomada no handshake. Olhar o body nesse momento é olhar para o lugar errado.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A stack é o sinal, não a linguagem.&lt;/strong&gt; Dois &lt;code&gt;curl&lt;/code&gt; na mesma máquina, fingerprints diferentes. Um upgrade de biblioteca muda a sua identidade na rede, e ninguém te avisa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JA3 de browser morreu em 2023, e foi de propósito.&lt;/strong&gt; Não é bug, não é falta de manutenção: é uma decisão de engenharia do Chrome que a Fastly mediu e a Cloudflare comentou. Quem ainda constrói detecção em cima de JA3 de browser está detectando um alvo que se move a cada conexão.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E o fingerprint nunca é a história inteira.&lt;/strong&gt; Ele é uma chave numa decisão que também olha prevalência, ASN, rate, cookie e comportamento. Confundir “meu TLS está errado” com “meu IP está queimado” é o jeito mais rápido de passar a tarde inteira ajustando cipher sem mudar nada.&lt;/p&gt;
&lt;p&gt;No próximo post eu faço o caminho inverso: pego o Burp, mostro em três camadas por que ele anuncia que é uma ferramenta Java, e o que dá para fazer a respeito — incluindo o que não dá, e por quê.&lt;/p&gt;</content:encoded><category>tls</category><category>fingerprint</category><category>ja3</category><category>ja4</category><category>cloudflare</category></item><item><title>omp: do zero ao /vibe</title><link>https://flameee-sec.netlify.app/pt-br/posts/configuring-omp/</link><guid isPermaLink="true">https://flameee-sec.netlify.app/pt-br/posts/configuring-omp/</guid><description>Instalação, tema, provedores, modelos e o /vibe: o passo a passo do meu setup do omp em macOS, Linux e Windows.</description><pubDate>Thu, 22 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Primeiro post de verdade aqui, e é o tutorial que eu queria ter tido: sair do zero e chegar no meu setup do &lt;strong&gt;omp&lt;/strong&gt; (&lt;a href=&quot;https://github.com/can1357/oh-my-pi&quot;&gt;oh-my-pi&lt;/a&gt;), bonito e configurado, passo a passo. Tudo foi conferido contra a documentação e o CLI do omp (18.12.5 aqui; o caminho Linux foi testado num container Ubuntu 24.04 limpo, que instalou a 18.12.6).&lt;/p&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-note&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;NOTE&lt;/p&gt;
&lt;p&gt;O guia cobre &lt;strong&gt;macOS&lt;/strong&gt;, &lt;strong&gt;Linux&lt;/strong&gt; (Debian/Ubuntu) e &lt;strong&gt;Windows&lt;/strong&gt;. No Windows, o omp roda nativo no PowerShell, segundo o README oficial; Oh My Zsh e Powerlevel10k só existem dentro do WSL2. Passos iguais em todo sistema aparecem num bloco só.&lt;/p&gt;
&lt;/aside&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Passo&quot;&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Passo&lt;/th&gt;&lt;th&gt;macOS&lt;/th&gt;&lt;th&gt;Linux (Debian/Ubuntu)&lt;/th&gt;&lt;th&gt;Windows&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Gerenciador de pacotes&lt;/td&gt;&lt;td&gt;Homebrew&lt;/td&gt;&lt;td&gt;apt&lt;/td&gt;&lt;td&gt;winget&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Terminal&lt;/td&gt;&lt;td&gt;Warp (o meu)&lt;/td&gt;&lt;td&gt;Warp ou qualquer um com Nerd Font&lt;/td&gt;&lt;td&gt;Windows Terminal (ou Warp)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Nerd Font&lt;/td&gt;&lt;td&gt;&lt;code&gt;brew install --cask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;zip em &lt;code&gt;~/.local/share/fonts&lt;/code&gt; + &lt;code&gt;fc-cache&lt;/code&gt;&lt;/td&gt;&lt;td&gt;zip → Instalar&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tema do shell&lt;/td&gt;&lt;td&gt;Oh My Zsh + p10k&lt;/td&gt;&lt;td&gt;Oh My Zsh + p10k&lt;/td&gt;&lt;td&gt;só dentro do WSL2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;omp&lt;/td&gt;&lt;td&gt;&lt;code&gt;brew install can1357/tap/omp&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;curl -fsSL https://omp.sh/install | sh&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;irm https://omp.sh/install.ps1 | iex&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;o-que-é-um-harness&quot;&gt;O que é um harness&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#o-que-é-um-harness&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Se você só usou LLM pela janela de chat, a peça que falta é esta. O modelo, sozinho, é texto entra, texto sai. Ele não lê seu repositório, não roda comando, não lembra da semana passada. O &lt;strong&gt;harness&lt;/strong&gt; é o programa em volta dele que o transforma num agente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;O loop do agente.&lt;/strong&gt; O modelo pede uma chamada de ferramenta, o harness executa e devolve o resultado, e isso se repete até a tarefa acabar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;As ferramentas.&lt;/strong&gt; Ler e editar arquivo, shell, busca, browser, servidores MCP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;O que entra no contexto.&lt;/strong&gt; System prompt, regras do projeto (&lt;code&gt;AGENTS.md&lt;/code&gt;), skills, memória, conteúdo de arquivos, e a compactação quando o contexto enche.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissões e aprovações.&lt;/strong&gt; O que roda sozinho e o que precisa do seu “ok”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sessões e histórico.&lt;/strong&gt; Retomar de onde parou.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subagentes e orquestração.&lt;/strong&gt; Dividir o trabalho entre vários agentes (no omp, isso inclui o &lt;code&gt;/vibe&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Roteamento de modelos.&lt;/strong&gt; Um modelo forte para pensar, um barato para o braçal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pensa num console: o modelo é a CPU; o harness é todo o resto, controle, memory card e leitor de disco.&lt;/p&gt;
&lt;p&gt;Na prática, o agente &lt;strong&gt;age&lt;/strong&gt; em vez de sugerir: roda o comando e lê a saída, sem você copiar e colar. Ele &lt;strong&gt;segura estado&lt;/strong&gt; numa tarefa longa. E regras, skills e contexto de projeto fazem o agente seguir o &lt;strong&gt;seu método&lt;/strong&gt; toda vez, sem você colar as mesmas instruções em cada conversa. Trabalho em paralelo vira subagente ou worker. Pela minha experiência, o mesmo modelo parece outro dependendo das ferramentas e de como o harness cuida do contexto.&lt;/p&gt;
&lt;p&gt;O que ele &lt;strong&gt;não&lt;/strong&gt; faz: não substitui julgamento e não transforma modelo ruim em modelo bom.&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Aspecto&quot;&gt;


































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Aspecto&lt;/th&gt;&lt;th&gt;Chat&lt;/th&gt;&lt;th&gt;Harness&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Roda comandos&lt;/td&gt;&lt;td&gt;não&lt;/td&gt;&lt;td&gt;sim, com aprovação configurável&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Edita arquivos&lt;/td&gt;&lt;td&gt;você copia e cola&lt;/td&gt;&lt;td&gt;direto no disco&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Regras do projeto&lt;/td&gt;&lt;td&gt;você cola toda vez&lt;/td&gt;&lt;td&gt;carregadas sozinhas&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Agentes em paralelo&lt;/td&gt;&lt;td&gt;não&lt;/td&gt;&lt;td&gt;subagentes e workers&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Escolha de modelo&lt;/td&gt;&lt;td&gt;o que o app oferece&lt;/td&gt;&lt;td&gt;qualquer provedor, um por tarefa&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Exemplos da categoria: Claude Code, Codex CLI, opencode, Aider, o agente do Cursor e o omp.&lt;/p&gt;
&lt;h3 id=&quot;por-que-o-omp&quot;&gt;Por que o omp&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#por-que-o-omp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pra mim, o omp ganha por juntar tudo isso num lugar só: dezenas de provedores (por variável de ambiente, login OAuth ou qualquer gateway compatível), modelos locais detectados sozinhos, &lt;strong&gt;papéis de modelo&lt;/strong&gt; com nível de thinking por papel, subagentes customizáveis em markdown, o &lt;strong&gt;&lt;code&gt;/vibe&lt;/code&gt;&lt;/strong&gt;, skills, memória entre sessões, MCP, modos de aprovação, ofuscação de segredos, perfis isolados e sessões que dá para retomar. E ainda é bonito no terminal, que é metade deste post.&lt;/p&gt;
&lt;h2 id=&quot;1-pré-requisitos-e-terminal&quot;&gt;1. Pré-requisitos e terminal&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#1-pré-requisitos-e-terminal&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O omp não precisa de runtime: o Homebrew e os scripts oficiais entregam um binário pronto. O Bun (≥ 1.3.14, segundo o README) só entra se você preferir instalar com &lt;code&gt;bun install -g @oh-my-pi/pi-coding-agent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;O terminal precisa aceitar uma fonte customizada. No macOS eu uso o &lt;a href=&quot;https://www.warp.dev/download&quot;&gt;Warp&lt;/a&gt;, que também tem versão para Linux e Windows. No Windows, o padrão sensato é o Windows Terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt; (Homebrew, comando oficial do &lt;a href=&quot;https://brew.sh&quot;&gt;brew.sh&lt;/a&gt;, e o Warp)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;/bin/bash&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; --cask&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; warp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; (o básico que os próximos passos usam; terminal: Warp pelo site ou o que você já usa)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; apt&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; update&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; sudo&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; apt&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -y&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; curl&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; unzip&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; fontconfig&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt; (PowerShell; o Windows Terminal também vem da Microsoft Store)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;POWERSHELL&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;powershell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;winget install &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;id Microsoft.WindowsTerminal &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;e&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;2-nerd-font&quot;&gt;2. Nerd Font&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#2-nerd-font&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Uma Nerd Font é uma fonte de programação com milhares de ícones extras embutidos: logo do git, pasta, setas powerline, spinners. O omp usa esses ícones quando você liga &lt;code&gt;symbolPreset: nerd&lt;/code&gt;, o status line tem um preset &lt;code&gt;nerd&lt;/code&gt;, e o Powerlevel10k do passo 3 também depende deles. Sem a fonte, os ícones viram quadradinhos.&lt;/p&gt;
&lt;p&gt;Eu uso a &lt;strong&gt;JetBrains Mono Nerd Font&lt;/strong&gt;, na variante &lt;strong&gt;NL&lt;/strong&gt; (sem ligaduras), cujo nome de família é &lt;code&gt;JetBrainsMonoNL Nerd Font&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; --cask&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; font-jetbrains-mono-nerd-font&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; (método manual do projeto nerd-fonts; &lt;code&gt;fc-list&lt;/code&gt; confirma)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; ~/.local/share/fonts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -fsSLo&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; /tmp/JetBrainsMono.zip&lt;/span&gt;&lt;span style=&quot;color:#FF7BD0&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;  https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;unzip&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -oq&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; /tmp/JetBrainsMono.zip&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; ~/.local/share/fonts/JetBrainsMono&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;fc-cache&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -fv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;fc-list&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;JetBrainsMonoNL Nerd Font&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;No Arch, o pacote oficial é &lt;code&gt;sudo pacman -S ttf-jetbrains-mono-nerd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt; (baixe &lt;code&gt;JetBrainsMono.zip&lt;/code&gt; em &lt;a href=&quot;https://github.com/ryanoasis/nerd-fonts/releases/latest&quot;&gt;github.com/ryanoasis/nerd-fonts/releases/latest&lt;/a&gt;, extraia, selecione os &lt;code&gt;.ttf&lt;/code&gt; e clique com o botão direito → &lt;strong&gt;Instalar&lt;/strong&gt;). Depois, no &lt;code&gt;settings.json&lt;/code&gt; do Windows Terminal (Configurações com &lt;kbd&gt;Shift&lt;/kbd&gt; pressionado), a fonte vale para todos os perfis em &lt;code&gt;profiles.defaults&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;JSON&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;profiles&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;defaults&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;font&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;face&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;JetBrainsMonoNL Nerd Font&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 13&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Se você usa WSL2, a fonte é instalada no &lt;strong&gt;Windows&lt;/strong&gt;: quem desenha os ícones é o Windows Terminal, não o Linux.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Selecionar no terminal.&lt;/strong&gt; No Warp (qualquer sistema), escolha &lt;code&gt;JetBrainsMonoNL Nerd Font&lt;/code&gt; nas configurações de aparência. No macOS o meu &lt;code&gt;~/.warp/settings.toml&lt;/code&gt; fica assim:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TOML&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;appearance&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;font_family &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;JetBrainsMonoNL Nerd Font&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;font_size &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 13.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;appearance&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;themes&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;theme &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;dark&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;appearance&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6;font-weight:bold&quot;&gt;cursor&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;cursor_display_type &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;bar&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Em outro terminal (iTerm2, Ghostty, Kitty, GNOME Terminal…), o princípio é o mesmo: a fonte do terminal tem que ser uma Nerd Font.&lt;/p&gt;
&lt;h2 id=&quot;3-a-cara-do-shell-opcional&quot;&gt;3. A cara do shell (opcional)&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#3-a-cara-do-shell-opcional&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Eu uso Oh My Zsh com o tema Powerlevel10k. Não é necessário para o omp, mas faz parte da cara do meu terminal. Zsh, Oh My Zsh e p10k são coisa de shell Unix: no Windows eles funcionam dentro do WSL2, não no PowerShell.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; o zsh já é o shell padrão; nada a instalar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; (e Windows dentro do WSL2: instale com &lt;code&gt;wsl --install&lt;/code&gt; num PowerShell de administrador, reinicie, abra o Ubuntu e siga daqui)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; apt&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -y&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; zsh&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; curl&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;chsh&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;which&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; zsh&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;   # vale no próximo login&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;macOS, Linux e WSL2:&lt;/strong&gt; a partir daqui é igual. Oh My Zsh e Powerlevel10k como tema dele, com os comandos dos READMEs oficiais:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;sh&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; --depth=1&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; https://github.com/romkatv/powerlevel10k.git&lt;/span&gt;&lt;span style=&quot;color:#FF7BD0&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:-$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOME&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;.oh-my-zsh&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;custom&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;/themes/powerlevel10k&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;No &lt;code&gt;~/.zshrc&lt;/code&gt;, troque o tema (eu deixo só o plugin &lt;code&gt;git&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;ZSH_THEME&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;powerlevel10k/powerlevel10k&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;plugins&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=(&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Abra um terminal novo; o wizard começa sozinho. Se não começar, rode &lt;code&gt;p10k configure&lt;/code&gt;. As minhas escolhas foram: &lt;code&gt;nerdfont-v3 + powerline&lt;/code&gt;, ícones pequenos, estilo &lt;code&gt;rainbow&lt;/code&gt;, relógio 24h, separadores angulares, duas linhas, &lt;em&gt;transient prompt&lt;/em&gt; e &lt;em&gt;instant prompt&lt;/em&gt;. O wizard grava tudo em &lt;code&gt;~/.p10k.zsh&lt;/code&gt;. No Warp, o prompt do p10k só aparece com &lt;code&gt;honor_ps1 = true&lt;/code&gt; em &lt;code&gt;[terminal.input]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;4-instalando-o-omp&quot;&gt;4. Instalando o omp&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#4-instalando-o-omp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt; (é como eu instalo)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; can1357/tap/omp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; (script oficial; instala em &lt;code&gt;~/.local/bin&lt;/code&gt;, que precisa estar no &lt;code&gt;PATH&lt;/code&gt;)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; https://omp.sh/install&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt; PATH&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOME/.local/bin:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;PATH&quot;&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;   # coloque no ~/.zshrc ou ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt; (PowerShell, script oficial; dentro do WSL2, use o comando do Linux)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;POWERSHELL&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;powershell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;irm https:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;omp.sh&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;install.ps1 &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt; iex&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Em qualquer sistema, confira com &lt;code&gt;omp --version&lt;/code&gt;. &lt;code&gt;omp setup&lt;/code&gt; roda o wizard de onboarding; depois é só &lt;code&gt;omp&lt;/code&gt; dentro de um projeto. Tudo do usuário mora em &lt;code&gt;~/.omp/agent/&lt;/code&gt; (&lt;code&gt;omp config path&lt;/code&gt; mostra onde): &lt;code&gt;config.yml&lt;/code&gt;, &lt;code&gt;models.yml&lt;/code&gt;, &lt;code&gt;mcp.json&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;skills/&lt;/code&gt;, &lt;code&gt;agents/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Um provedor.&lt;/strong&gt; OAuth (assinatura) é igual em todo sistema: &lt;code&gt;omp login&lt;/code&gt; e escolha na lista (dentro da sessão, &lt;code&gt;/login&lt;/code&gt;). Para API key, deixe a chave fora do histórico do shell:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS e Linux&lt;/strong&gt; (funciona em zsh e bash)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;API key: &apos;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -rs&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; K&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; printf&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;\nANTHROPIC_API_KEY=%s\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;K&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; ~/.omp/agent/.env&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; unset&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; K&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;chmod&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; ~/.omp/agent/.env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; models&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;   # o que ficou disponível, agrupado por provedor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt; (PowerShell 7.1+; grava uma variável de ambiente do usuário, que vale nos próximos terminais)&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;POWERSHELL&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;powershell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;k &lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; Read-Host&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;API key&apos;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;MaskInput&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;Environment&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;::SetEnvironmentVariable&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&apos;ANTHROPIC_API_KEY&apos;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; $&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;User&apos;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;Remove-Variable&lt;/span&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt; k&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;O omp lê &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;, &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;, &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt;, &lt;code&gt;DEEPSEEK_API_KEY&lt;/code&gt; e várias outras; &lt;code&gt;omp --help&lt;/code&gt; lista as principais.&lt;/p&gt;
&lt;h2 id=&quot;5-deixando-o-omp-bonito&quot;&gt;5. Deixando o omp bonito&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#5-deixando-o-omp-bonito&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Daqui até o fim, tudo é &lt;strong&gt;igual nos três sistemas&lt;/strong&gt;: é config do omp, não do SO. O visual fica em &lt;code&gt;~/.omp/agent/config.yml&lt;/code&gt;. Estes são os valores que eu mudei do padrão:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;YAML&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;# ~/.omp/agent/config.yml — aparência&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  dark&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; dark-dracula&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # tema embutido (padrão: titanium)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;symbolPreset&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; nerd&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # ícones da Nerd Font (padrão: unicode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;statusLine&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  preset&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; nerd&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # ícones no status line (padrão: default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  contextLine&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; annotated&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # uso de contexto anotado&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;composer&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  shape&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; box&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # editor numa caixa (padrão: band)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;display&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  showTokenUsage&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # tokens por resposta&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  showTurnTime&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # tempo total de cada turno, ferramentas incluídas&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ou pelo CLI, uma chave por vez (o nome precisa bater exatamente com o schema):&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; theme.dark&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; dark-dracula&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; symbolPreset&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; nerd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; statusLine.preset&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; nerd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; statusLine.contextLine&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; annotated&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; composer.shape&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; box&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; display.showTokenUsage&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;omp&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; display.showTurnTime&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tudo isso também está em &lt;code&gt;/settings&lt;/code&gt;, dentro da sessão, se você preferir menu. Outras opções de &lt;code&gt;statusLine.preset&lt;/code&gt;: &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;compact&lt;/code&gt;, &lt;code&gt;full&lt;/code&gt;, &lt;code&gt;ascii&lt;/code&gt;, &lt;code&gt;custom&lt;/code&gt;. O separador fica em &lt;code&gt;statusLine.separator&lt;/code&gt; (&lt;code&gt;powerline&lt;/code&gt;, &lt;code&gt;powerline-thin&lt;/code&gt;, &lt;code&gt;slash&lt;/code&gt;, &lt;code&gt;pipe&lt;/code&gt;, &lt;code&gt;block&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;ascii&lt;/code&gt;). Temas próprios são arquivos JSON em &lt;code&gt;~/.omp/agent/themes/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resultado esperado:&lt;/strong&gt; ao abrir &lt;code&gt;omp&lt;/code&gt;, o editor aparece numa caixa, o status line mostra modelo, pasta e git com ícones, e as cores são as do Dracula. Se aparecerem quadradinhos, a fonte do passo 2 não está selecionada.&lt;/p&gt;
&lt;h2 id=&quot;6-configs-que-valem-a-pena-além-do-visual&quot;&gt;6. Configs que valem a pena além do visual&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#6-configs-que-valem-a-pena-além-do-visual&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As minhas, todas fora do padrão:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Chave&quot;&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Chave&lt;/th&gt;&lt;th&gt;Valor&lt;/th&gt;&lt;th&gt;O que faz&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;defaultThinkingLevel&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;max&lt;/code&gt;&lt;/td&gt;&lt;td&gt;profundidade de raciocínio padrão (padrão: &lt;code&gt;high&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;temperature&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0.7&lt;/code&gt;&lt;/td&gt;&lt;td&gt;temperatura de amostragem (padrão: a do provedor)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;memory.backend&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;&lt;td&gt;extrai lições das sessões e injeta um resumo na próxima (padrão: &lt;code&gt;off&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;autolearn.enabled&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;deixa o agente criar e atualizar skills gerenciadas&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;autolearn.autoContinue&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;roda um turno privado de captura de lições ao parar; gasta token&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;model.loopGuard.enabled&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;&lt;td&gt;desliga o detector automático de loop no raciocínio e no texto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;model.toolCallLoopGuard.threshold&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;20&lt;/code&gt;&lt;/td&gt;&lt;td&gt;chamadas idênticas seguidas antes de intervir (padrão: &lt;code&gt;5&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;bash.allowCompoundCommands&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;avalia cada comando de uma cadeia &lt;code&gt;&amp;#x26;&amp;#x26;&lt;/code&gt; separadamente&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;browser.headless&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;&lt;td&gt;mostra a janela quando o agente usa o browser&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;computer.enabled&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;libera o controle do desktop (screenshots, input)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Duas que eu recomendo para todo mundo, mesmo não estando na minha lista:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;secrets.enabled: true&lt;/code&gt;&lt;/strong&gt; (padrão &lt;code&gt;false&lt;/code&gt;): env vars com cara de credencial e padrões conhecidos (JWT, Bearer, chaves de cloud) viram placeholders no texto enviado ao provedor e voltam ao normal na hora de executar a ferramenta. Não pega valor curto nem o prompt estático.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;tools.approvalMode&lt;/code&gt;&lt;/strong&gt;: o padrão é &lt;code&gt;yolo&lt;/code&gt; (roda tudo). Se você está começando, &lt;code&gt;write&lt;/code&gt; aprova leitura e escrita e pergunta antes de executar. Subagentes sempre rodam em &lt;code&gt;yolo&lt;/code&gt;; a aprovação que conta é a do pai.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-tip&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;TIP&lt;/p&gt;
&lt;p&gt;&lt;code&gt;omp --profile hunt --alias omp-hunt&lt;/code&gt; cria um perfil isolado (auth, sessões e settings próprios) e um atalho de shell para ele. É um bom jeito de separar um setup mais agressivo do seu omp do dia a dia.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 id=&quot;7-provedores-e-modelos&quot;&gt;7. Provedores e modelos&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#7-provedores-e-modelos&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O omp trabalha com &lt;strong&gt;papéis&lt;/strong&gt;, configurados em &lt;code&gt;modelRoles&lt;/code&gt; no &lt;code&gt;config.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Papel&quot;&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Papel&lt;/th&gt;&lt;th&gt;Para quê&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;&lt;td&gt;a conversa principal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;smol&lt;/code&gt;&lt;/td&gt;&lt;td&gt;tarefas leves; workers &lt;code&gt;fast&lt;/code&gt; do &lt;code&gt;/vibe&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;slow&lt;/code&gt;&lt;/td&gt;&lt;td&gt;raciocínio pesado&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;plan&lt;/code&gt;&lt;/td&gt;&lt;td&gt;o plan mode (&lt;code&gt;/plan&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;task&lt;/code&gt;&lt;/td&gt;&lt;td&gt;subagentes e workers &lt;code&gt;good&lt;/code&gt; do &lt;code&gt;/vibe&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;vision&lt;/code&gt;&lt;/td&gt;&lt;td&gt;analisar imagens&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;commit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;omp commit&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;tiny&lt;/code&gt; / &lt;code&gt;memory&lt;/code&gt;&lt;/td&gt;&lt;td&gt;títulos de sessão e pipeline de memória&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;O sufixo &lt;code&gt;:nível&lt;/code&gt; define o thinking por papel (&lt;code&gt;off&lt;/code&gt;, &lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;xhigh&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;): &lt;code&gt;default: deepseek/deepseek-v4-flash:max&lt;/code&gt;. &lt;code&gt;smol&lt;/code&gt; e &lt;code&gt;slow&lt;/code&gt; sem valor herdam o &lt;code&gt;default&lt;/code&gt;. Para trocar: &lt;code&gt;/model&lt;/code&gt; abre o hub, &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt; alterna entre os modelos dos papéis, &lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;Tab&lt;/kbd&gt; alterna o thinking, e &lt;code&gt;omp --model opus&lt;/code&gt; aceita busca aproximada.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gateway próprio&lt;/strong&gt; (qualquer endpoint compatível com OpenAI ou Anthropic) vai em &lt;code&gt;~/.omp/agent/models.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;YAML&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;providers&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  my-gateway&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    baseUrl&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; https://gateway.example/v1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    api&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; openai-completions&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # ou anthropic-messages, openai-responses…&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    apiKey&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; MY_GATEWAY_API_KEY&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # nome da env var; o omp usa o valor dela&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    authHeader&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # Authorization: Bearer &amp;#x3C;chave&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    models&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      -&lt;/span&gt;&lt;span style=&quot;color:#F0525A&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; vendor/big-model&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt; # o id que o endpoint espera&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; Big Model&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        reasoning&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        thinking&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F0525A&quot;&gt; mode&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; effort&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F0525A&quot;&gt; efforts&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;low&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; high&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; max&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;],&lt;/span&gt;&lt;span style=&quot;color:#F0525A&quot;&gt; defaultLevel&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; high&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        input&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; image&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        contextWindow&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 1048576&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;        maxTokens&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 131072&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;apiKey&lt;/code&gt; é “nome de env var ou literal”: se bater com uma variável preenchida, vale o valor dela. Um erro de YAML faz o omp ignorar o arquivo inteiro, então sempre confira com &lt;code&gt;omp models my-gateway&lt;/code&gt;. E antes de declarar um provedor, rode &lt;code&gt;omp models &amp;#x3C;nome&gt;&lt;/code&gt;: se ele já vem embutido, só falta a credencial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O que eu uso:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Papel &lt;code&gt;default&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;deepseek/deepseek-v4-flash:max&lt;/code&gt;, pelo provedor DeepSeek embutido. Tudo que herda o &lt;code&gt;default&lt;/code&gt; (os workers &lt;code&gt;fast&lt;/code&gt;, por exemplo) roda nele.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sessão principal e diretor do &lt;code&gt;/vibe&lt;/code&gt;&lt;/strong&gt;: Claude Opus (&lt;code&gt;anthropic/claude-opus-5-5&lt;/code&gt;). O omp permite trocar o modelo só da sessão sem mexer no papel &lt;code&gt;default&lt;/code&gt;: &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt; escolhe um modelo temporário para a sessão, e &lt;code&gt;omp --model opus&lt;/code&gt; faz o mesmo na linha de comando.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gateway no &lt;code&gt;models.yml&lt;/code&gt;&lt;/strong&gt; com DeepSeek V4.1 Flash, Nemotron 3.5 Super VL 120B e GLM-5.3 Flash, todos com 1M de contexto, a escada de effort declarada e custo preenchido para o omp contabilizar.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;8-vibe&quot;&gt;8. /vibe&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#8-vibe&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/vibe&lt;/code&gt; transforma a sessão num &lt;strong&gt;diretor&lt;/strong&gt;. O diretor fica só com &lt;code&gt;read&lt;/code&gt;, um &lt;code&gt;todo&lt;/code&gt; opcional e cinco ferramentas de controle; quem busca, edita e roda são &lt;strong&gt;workers persistentes&lt;/strong&gt; em background, cada um com a própria transcrição.&lt;/p&gt;
&lt;figure&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;./vibe-director.svg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Diagrama do /vibe: o diretor dispara workers fast e good, que trabalham no workspace; o diretor verifica lendo os arquivos&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;figcaption&gt;O diretor lê e decide; os workers fazem.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/vibe                       # entra no modo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/vibe migra o parser        # entra e já manda a primeira diretiva&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/vibe                       # sai (encerra os workers)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Tabela: Tier&quot;&gt;






















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tier&lt;/th&gt;&lt;th&gt;Agente&lt;/th&gt;&lt;th&gt;Papel&lt;/th&gt;&lt;th&gt;Uso&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;fast&lt;/td&gt;&lt;td&gt;sonic&lt;/td&gt;&lt;td&gt;@smol&lt;/td&gt;&lt;td&gt;execução mecânica, volume, rascunho&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;good&lt;/td&gt;&lt;td&gt;task&lt;/td&gt;&lt;td&gt;@task&lt;/td&gt;&lt;td&gt;julgamento, design, revisar o &lt;code&gt;fast&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;O diretor usa &lt;code&gt;vibe_spawn&lt;/code&gt; (cria um worker com brief e tier), &lt;code&gt;vibe_send&lt;/code&gt; (corrige o rumo ou manda o próximo passo), &lt;code&gt;vibe_wait&lt;/code&gt;, &lt;code&gt;vibe_kill&lt;/code&gt; e &lt;code&gt;vibe_list&lt;/code&gt;. Você só fala com ele em linguagem natural.&lt;/p&gt;
&lt;p&gt;Para escolher os modelos dos tiers, a documentação sugere aliases:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;YAML&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;task&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  agentModelOverrides&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    sonic&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;@fast_worker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;    task&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &apos;@good_worker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;modelRoles&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  fast_worker&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; my-gateway/vendor/fast-model&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F0525A&quot;&gt;  good_worker&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; my-gateway/vendor/big-model:high&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Esse override vale para todo subagente &lt;code&gt;sonic&lt;/code&gt; e &lt;code&gt;task&lt;/code&gt;, não só para o &lt;code&gt;/vibe&lt;/code&gt;. No meu caso não configurei nada: o &lt;code&gt;fast&lt;/code&gt; herda o &lt;code&gt;default&lt;/code&gt; (DeepSeek V4 Flash) e o &lt;code&gt;good&lt;/code&gt; cai no modelo ativo do diretor (Opus). Braçal barato, julgamento caro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Como brifar.&lt;/strong&gt; O worker não vê a conversa do diretor. Dê arquivos, restrições e um critério de aceite que dê para checar:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Objetivo: paginação por cursor em GET /api/orders.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Arquivos: src/api/orders.ts, tests/orders.test.ts. Não mexa em outros.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Aceite: `npm test -- orders` passa, com um teste novo para a última página.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Entregue: arquivos tocados e a saída do teste.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Como verificar.&lt;/strong&gt; O próprio doc diz: worker que terminou significa que o turno acabou, não que ele acertou. O diretor lê os arquivos tocados e a saída completa em &lt;code&gt;agent://&amp;#x3C;id&gt;&lt;/code&gt;. Um worker por frente de trabalho, &lt;code&gt;fast&lt;/code&gt; e &lt;code&gt;good&lt;/code&gt; em paralelo, e o &lt;code&gt;good&lt;/code&gt; revisando o &lt;code&gt;fast&lt;/code&gt; quando importa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Armadilhas:&lt;/strong&gt; sair do &lt;code&gt;/vibe&lt;/code&gt; encerra todos os workers; não combina com plan mode nem goal mode; com ele ativo, não dá para criar, apagar, forkar ou mover a sessão. Este post, inclusive, foi rascunhado por um worker do omp numa sessão de diretor.&lt;/p&gt;
&lt;h2 id=&quot;9-skills-mcp-e-agentsmd&quot;&gt;9. Skills, MCP e AGENTS.md&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#9-skills-mcp-e-agentsmd&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AGENTS.md&lt;/strong&gt;: o omp carrega &lt;code&gt;~/.omp/agent/AGENTS.md&lt;/code&gt;, &lt;code&gt;.omp/AGENTS.md&lt;/code&gt; e um &lt;code&gt;AGENTS.md&lt;/code&gt; solto, procurado do diretório atual até a raiz do repo (lê &lt;code&gt;CLAUDE.md&lt;/code&gt; também). Mantenha curto: regras duras e onde está o resto.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills&lt;/strong&gt;: um diretório com &lt;code&gt;SKILL.md&lt;/code&gt; (&lt;code&gt;name&lt;/code&gt; e &lt;code&gt;description&lt;/code&gt; no frontmatter). Só nome e descrição vão para o prompt; o corpo carrega sob demanda (&lt;code&gt;skill://&amp;#x3C;nome&gt;&lt;/code&gt;). As suas ficam em &lt;code&gt;~/.omp/agent/skills/&lt;/code&gt;; com &lt;code&gt;autolearn&lt;/code&gt;, o agente escreve as dele em &lt;code&gt;~/.omp/agent/managed-skills/&lt;/code&gt;. Eu tenho umas 30 minhas (classes de vulnerabilidade, metodologia, triagem, escrita de relatório) e dezenas de gerenciadas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP&lt;/strong&gt;: &lt;code&gt;~/.omp/agent/mcp.json&lt;/code&gt;, com &lt;code&gt;${VAR}&lt;/code&gt; expandido. Eu uso o Burp Suite, pela extensão oficial &lt;a href=&quot;https://github.com/PortSwigger/mcp-server&quot;&gt;MCP Server da PortSwigger&lt;/a&gt; (SSE local, por padrão em &lt;code&gt;http://127.0.0.1:9876&lt;/code&gt;), e uma base de conhecimento privada, um servidor MCP em Python sobre as minhas anotações:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;JSON&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;mcpServers&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;burp&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;sse&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;http://127.0.0.1:9876&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;kb&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;stdio&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;${HOME}/kb/.venv/bin/python&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;&quot;${HOME}/kb/server.py&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E8A87C&quot;&gt;KB_PATH&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;${HOME}/kb/data&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;10-um-gostinho-bug-bounty&quot;&gt;10. Um gostinho: bug bounty&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#10-um-gostinho-bug-bounty&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;aside class=&quot;markdown-alert markdown-alert-warning&quot; dir=&quot;auto&quot;&gt;
&lt;p class=&quot;markdown-alert-title&quot; dir=&quot;auto&quot;&gt;&lt;svg class=&quot;octicon&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot; height=&quot;16&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;WARNING&lt;/p&gt;
&lt;p&gt;Muitos programas restringem o compartilhamento de dados de vulnerabilidade ou do alvo com terceiros, e um provedor de LLM na nuvem pode ser um terceiro. Leia a política do programa antes. Prefira provedores ou configurações sem retenção de dados, ou modelos locais. Na dúvida, não mande.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Para programa grande eu abro vários terminais com omp, cada um com um papel, e eles conversam &lt;strong&gt;só pelo filesystem&lt;/strong&gt;. Eu chamo isso de matilha: um &lt;code&gt;orch&lt;/code&gt; cuida do escopo e da fila, atacantes &lt;code&gt;a1..aN&lt;/code&gt; pegam um host por vez, e um &lt;code&gt;triage&lt;/code&gt; valida os candidatos. Toda escrita compartilhada passa por um script bash, o &lt;code&gt;bin/hb&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;TEXT&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;~/hunting/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── PROTOCOL.md            # o contrato: papéis, lease, rate, prova&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── bin/hb                 # o barramento&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── scope/&amp;#x3C;program&gt;/       # eligible.txt, deny.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;└── state/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── queue.jsonl        # fila append-only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── claims/&amp;#x3C;host&gt;/     # lease por mkdir atômico (dono + epoch)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── findings.jsonl     # tudo nasce &quot;candidate&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── dead-ends.md       # negativos verificados + controle usado&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── intel.md           # padrão reutilizável: &quot;[tag] fato → ação&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └── loot/F-0001/       # request.txt, response.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;O loop de um atacante:&lt;/p&gt;
&lt;div class=&quot;code-frame&quot;&gt;&lt;div class=&quot;code-frame__bar&quot;&gt;&lt;span class=&quot;code-frame__lang&quot;&gt;BASH&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;code-frame__copy&quot; hidden data-copy=&quot;&quot;&gt;COPY&lt;/button&gt;&lt;/div&gt;&lt;pre class=&quot;astro-code psx-night&quot; style=&quot;background-color:#0b0c0c;color:#e6e0d1; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E6E0D1&quot;&gt;HOST&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;=$(&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;bin/hb&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; a1&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; exit&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;     # lease atômico do próximo host&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;bin/hb&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; inscope&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST&quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; bin/hb&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; release&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; a1&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt; exit&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;grep&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; state/dead-ends.md&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; state/intel.md&lt;/span&gt;&lt;span style=&quot;color:#8A877E;font-style:italic&quot;&gt;   # não repetir teste morto&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;ffuf&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;https://&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST/FUZZ&quot;&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -w&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; wordlists/base.txt&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -ac&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -rate&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 20&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;color:#F2C230&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;recon/&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST/ffuf.json&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;bin/hb&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; finding&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; a1&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; idor&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;https://&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST/api/orders?id=2&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;BOLA em /orders&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;200 com pedido da conta B&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#5FD3C6&quot;&gt;bin/hb&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; done&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A39F93&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt;HOST&quot;&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; a1&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; findings&lt;/span&gt;&lt;span style=&quot;color:#7BD36B&quot;&gt; &quot;1 candidate&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Os guardrails são executáveis, não promessas do modelo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escopo&lt;/strong&gt;: &lt;code&gt;hb inscope&lt;/code&gt; devolve exit &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt; ou &lt;code&gt;3&lt;/code&gt;; deny vence allow; host não listado não é tocado.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rate&lt;/strong&gt;: cap explícito por ferramenta, request manual a 1/s por host, e 403/429 em rajada pausa o host por 10 minutos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prova antes de alegação&lt;/strong&gt;: sem request e response em &lt;code&gt;state/loot/&lt;/code&gt;, é só candidato. A triagem reproduz do zero com controle negativo, num modelo de outra família.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revisão humana&lt;/strong&gt;: o agente pode rascunhar o relatório, mas a reprodução manual, a revisão e o envio são meus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Isso rende um post só para ele. Fica para o próximo.&lt;/p&gt;
&lt;h2 id=&quot;fechando&quot;&gt;Fechando&lt;a class=&quot;heading-anchor&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot; href=&quot;#fechando&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;O harness não pensa por você, mas faz o seu método rodar igual toda vez, e com um terminal que dá gosto de olhar. Recapitulando:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Um terminal com fonte customizável: Warp (macOS/Linux/Windows) ou Windows Terminal.&lt;/li&gt;
&lt;li&gt;JetBrainsMono Nerd Font: &lt;code&gt;brew install --cask&lt;/code&gt; no macOS, zip + &lt;code&gt;fc-cache&lt;/code&gt; no Linux, zip → Instalar no Windows; selecionada no terminal.&lt;/li&gt;
&lt;li&gt;(Opcional) zsh + Oh My Zsh + Powerlevel10k, &lt;code&gt;p10k configure&lt;/code&gt; (no Windows, dentro do WSL2).&lt;/li&gt;
&lt;li&gt;omp: &lt;code&gt;brew install can1357/tap/omp&lt;/code&gt;, &lt;code&gt;curl -fsSL https://omp.sh/install | sh&lt;/code&gt; ou &lt;code&gt;irm https://omp.sh/install.ps1 | iex&lt;/code&gt;; depois &lt;code&gt;omp setup&lt;/code&gt; e &lt;code&gt;omp login&lt;/code&gt; ou uma API key.&lt;/li&gt;
&lt;li&gt;Tema e ícones: &lt;code&gt;theme.dark&lt;/code&gt;, &lt;code&gt;symbolPreset&lt;/code&gt;, &lt;code&gt;statusLine.preset&lt;/code&gt;, &lt;code&gt;composer.shape&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Memória, segredos e aprovação.&lt;/li&gt;
&lt;li&gt;Papéis de modelo e, se precisar, um gateway no &lt;code&gt;models.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/vibe&lt;/code&gt; quando o básico estiver chato de tão confiável.&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>omp</category><category>ai-agents</category><category>tooling</category><category>terminal</category></item></channel></rss>