{
    "version" : "https://jsonfeed.org/version/1.1",
    "title" : "Misael Zapata",
    "description": "Site pessoal de Misael Zapata",
    "home_page_url" : "https://misael.org/",
    "feed_url" : "https://misael.org/pt/index.feed",
    "icon" : "https://misael.org/apple-touch-icon.png",
    "favicon" : "https://misael.org/favicon.ico",
    "author" : {
        "name" : "Misael Zapata",
        "url": "https://github.com/misaelzapata",
        "avatar": "https://misael.org/images/avatar.webp"
    },
    "items" : [
    {
        "title" : "A atualização que ninguém pediu",
        "date_published" : "2026-06-28T00:00:00Z",
        "date_modified" : "2026-06-28T00:00:00Z",
        "id" : "https://misael.org/pt/the-update-nobody-asked-for/",
        "url" : "https://misael.org/pt/the-update-nobody-asked-for/",
        "summary": "Os apps não se atualizam mais para melhorar — se atualizam para entrar no evento. Um passeio, com números reais e linkados, da febre de updates da Copa 2026 até como o FOMO que a sustenta foi projetado e massificado — e o ciclo de hype que fica cada vez mais curto, do offshoring aos \u0026lsquo;agentes\u0026rsquo; de IA.",
        "content_html" : "\u003cp\u003eAbra qualquer app esta semana e algo mudou de um dia para o outro. Chamei um Uber e o carrinho no mapa tinha a bandeira da minha seleção. Abri um app de delivery e pulou um cupom \u003cem\u003eporque alguém fez um gol\u003c/em\u003e do outro lado do planeta. Entrei no Duolingo para fazer minha lição e o Duo estava vestido com a camisa de uma seleção. Mandei uma mensagem com o emoji da bola e ele virou a bola oficial da Copa. Procurei um resultado no Google e a tela me jogou uma animação de comemoração.\u003c/p\u003e\n\u003cp\u003eNenhuma dessas atualizações consertou nada. Nenhuma deixou o app mais rápido, mais seguro ou mais útil. Todas existem pelo mesmo motivo: tem uma bola rolando em algum estádio e ninguém quer ficar de fora da conversa.\u003c/p\u003e\n\u003cp\u003eHouve um tempo em que uma atualização de software era uma promessa. Você esperava meses para \u003cem\u003eaquele\u003c/em\u003e bug ser corrigido, para a bateria durar um pouco mais, para a coisa parar de travar. A atualização chegava a milhões de pessoas, e por isso era levada a sério. Hoje fazemos deploy para esses mesmos milhões, coordenando cinco plataformas ao mesmo tempo, para colar uma bandeira num ícone. E na semana que vem vamos tirar, e ninguém vai lembrar que existiu.\u003c/p\u003e\n\u003cp\u003eEste artigo é sobre isso: o número, não o conteúdo. Sobre como uma ninharia acaba afetando alguém do outro lado do mundo e depois some tão rápido quanto apareceu. Sobre como a mola psicológica que faz isso funcionar foi \u003cem\u003edeliberadamente\u003c/em\u003e projetada. E sobre por que esse padrão é exatamente o mesmo do OpenClaw, das cripto, da IoT, do big data, e de quando mudamos o trabalho da América para a Ásia.\u003c/p\u003e\n\u003cdiv class=\"details admonition note open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z\"/\u003e\u003c/svg\u003e\u003c/span\u003eSobre as fontes\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003eTodo número neste artigo está linkado à sua fonte original, pública e gratuita. Se uma afirmação não tem link, é opinião minha e está escrita como tal. As imagens de apps são \u003cstrong\u003ereconstruções ilustrativas\u003c/strong\u003e (não capturas reais): o padrão importa mais que o pixel, e cada caso está citado ao seu anúncio oficial.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"a-copa-como-um-deploy-em-massa\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-copa-como-um-deploy-em-massa\" class=\"header-mark\" aria-label=\"Header mark for 'A Copa como um deploy em massa'\"\u003e\u003c/a\u003e1 A Copa como um deploy em massa\u003c/h2\u003e\u003cp\u003eVamos repassar o que aconteceu, com nome e sobrenome. Não foi um app — foram todos.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/world-cup-apps_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/world-cup-apps_pt.png\" data-sub-html=\"\u003ch2\u003eA mesma bola disparando um deploy em cada tela. Reconstrução ilustrativa; cada caso está linkado à sua fonte abaixo.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/world-cup-apps_pt.png\" srcset=\"/the-update-nobody-asked-for/images/world-cup-apps_pt_huf30041ffda05ed6f6cab5a3c59d42da7_92106_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/world-cup-apps_pt_huf30041ffda05ed6f6cab5a3c59d42da7_92106_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/world-cup-apps_pt_huf30041ffda05ed6f6cab5a3c59d42da7_92106_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"822\" width=\"1080\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eA mesma bola disparando um deploy em cada tela. Reconstrução ilustrativa; cada caso está linkado à sua fonte abaixo.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eA \u003cstrong\u003eUber\u003c/strong\u003e deixou você personalizar o ícone do carro no mapa com a bandeira da sua seleção, e lançou as \u003cem\u003e\u0026ldquo;Defeat Deals\u0026rdquo;\u003c/em\u003e: se o seu time é eliminado, ela te dá 30% de desconto numa corrida futura (\u003ca href=\"https://www.uber.com/us/en/newsroom/traveling-for-soccer-this-summer/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eUber Newsroom\u003c/a\u003e, \u003ca href=\"https://thepointsguy.com/news/uber-world-cup-features/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eThe Points Guy\u003c/a\u003e). O seu luto esportivo, virando cupom.\u003c/p\u003e\n\u003cp\u003eA \u003cstrong\u003ePedidosYa\u003c/strong\u003e (a gigante de delivery da América Latina) rodou a campanha \u003cem\u003e\u0026ldquo;Se tem gol, tem cupom\u0026rdquo;\u003c/em\u003e: cada vez que sai um gol, o app libera cupons em tempo real. A campanha promete distribuir, em média, mais de \u003cstrong\u003e2,7 bilhões de pesos\u003c/strong\u003e em cupons — mais de \u003cstrong\u003e77 milhões por dia de jogo\u003c/strong\u003e — em 14 países latino-americanos, de 11 de junho a 19 de julho (\u003ca href=\"https://www.infobae.com/america/agencias/2026/06/12/pedidosya-celebra-la-fiesta-del-futbol-entregando-millones-de-cupones-con-una-campana-inedita-si-hay-gol-hay-cupon/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eInfobae\u003c/a\u003e, \u003ca href=\"https://mercado.com.ar/marketing/pedidosya-activa-cupones-de-descuento-por-cada-gol-con-una-campana-regional\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eRevista Mercado\u003c/a\u003e). Um gol num estádio dispara uma chuva de cupons que faz milhões correrem para abrir o app antes que acabem. Isso é um evento de infraestrutura, disparado por uma bola.\u003c/p\u003e\n\u003cp\u003eO \u003cstrong\u003eDuolingo\u003c/strong\u003e montou a \u003cem\u003eDuo Cup\u003c/em\u003e: \u003cstrong\u003e48 uniformes de seleção\u003c/strong\u003e que você desbloqueia completando lições, num cronograma \u003cstrong\u003ealeatório\u003c/strong\u003e para que você não saiba qual está disponível e volte todo dia para conferir (\u003ca href=\"https://www.duolingo.com/duo_cup_suits_intro\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eDuolingo\u003c/a\u003e, \u003ca href=\"https://duolingo.fandom.com/wiki/Duo_Cup\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eDuolingo Wiki\u003c/a\u003e). O marketing é nacionalismo puro: \u003cem\u003e\u0026ldquo;a seleção precisa de você, desbloqueie seu uniforme\u0026rdquo;\u003c/em\u003e (\u003ca href=\"https://www.facebook.com/duolingo/posts/the-national-team-needs-you-unlock-your-duo-cup-avatar-suit-%EF%B8%8F/1440333504788285/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eDuolingo no Facebook\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eE os gigantes não ficaram de fora:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eO \u003cstrong\u003eWhatsApp\u003c/strong\u003e, com Adidas e FIFA, fez o emoji da bola virar a \u003cem\u003eTrionda\u003c/em\u003e, a bola oficial, durante todo o torneio — além de figurinhas temáticas, efeitos nas chamadas e um diretório de canais da Copa (\u003ca href=\"https://www.socialmediatoday.com/news/meta-announces-world-cup-features-across-its-apps/822741/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eSocial Media Today\u003c/a\u003e, \u003ca href=\"https://www.clarosports.com/futbol/mundial-2026/el-mundial-2026-llego-a-whatsapp-con-golazo-como-utilizar-el-nuevo-emoji-animado-del-balon-trionda/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eClaro Sports\u003c/a\u003e).\u003c/li\u003e\n\u003cli\u003eO \u003cstrong\u003eGoogle\u003c/strong\u003e rodou uma série de \u003cstrong\u003e69 Doodles\u003c/strong\u003e (36 artes únicas em 189 mercados, de 11 de junho a 20 de julho) e \u003cem\u003eeaster eggs\u003c/em\u003e na busca que comemoram sua vitória ou consolam sua derrota (\u003ca href=\"https://www.mediapost.com/publications/article/415745/google-touts-ai-search-for-world-cup-games.html\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMediaPost\u003c/a\u003e).\u003c/li\u003e\n\u003cli\u003eO \u003cstrong\u003eInstagram\u003c/strong\u003e somou alertas de placar ao vivo, figurinhas e efeitos temáticos (\u003ca href=\"https://www.socialmediatoday.com/news/meta-announces-world-cup-features-across-its-apps/822741/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eSocial Media Today\u003c/a\u003e).\u003c/li\u003e\n\u003cli\u003eO \u003cstrong\u003eTikTok\u003c/strong\u003e chegou a lançar o \u003cem\u003eTikTok Pro Events\u003c/em\u003e, \u003cstrong\u003eum app à parte\u003c/strong\u003e dedicado à Copa, onde você ganha \u0026ldquo;Stars\u0026rdquo; por participar (\u003ca href=\"https://techcrunch.com/2026/06/03/tiktok-launches-tiktok-pro-events-an-app-for-cultural-moments-like-the-fifa-world-cup/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eTechCrunch\u003c/a\u003e).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eUm app novo inteiro, para um evento de cinco semanas. Esse é o nível de investimento que uma bolinha dispara.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/app-shots_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/app-shots_pt.png\" data-sub-html=\"\u003ch2\u003eAssim apareciam alguns: o emoji Trionda e o «GOL» no WhatsApp, o Doodle de comemoração do Google, a campanha da PedidosYa, o TikTok Pro Events e o hub da Copa no Instagram. Da bandeirinha da Uber, por outro lado, não saiu nenhuma captura. Materiais de imprensa e oficiais, uso editorial.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/app-shots_pt.png\" srcset=\"/the-update-nobody-asked-for/images/app-shots_pt_hua175013f23c96d4c5ba7fa20ad60005f_367846_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/app-shots_pt_hua175013f23c96d4c5ba7fa20ad60005f_367846_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/app-shots_pt_hua175013f23c96d4c5ba7fa20ad60005f_367846_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"584\" width=\"1024\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eAssim apareciam alguns: o emoji Trionda e o «GOL» no WhatsApp, o Doodle de comemoração do Google, a campanha da PedidosYa, o TikTok Pro Events e o hub da Copa no Instagram. Da bandeirinha da Uber, por outro lado, não saiu nenhuma captura. Materiais de imprensa e oficiais, uso editorial.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003ch2 id=\"o-caso-duolingo-vendem-a-camisa-do-seu-país\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-caso-duolingo-vendem-a-camisa-do-seu-pa%c3%ads\" class=\"header-mark\" aria-label=\"Header mark for 'O caso Duolingo: vendem a camisa do seu país'\"\u003e\u003c/a\u003e2 O caso Duolingo: vendem a camisa do seu país\u003c/h2\u003e\u003cp\u003eO do Duolingo é o caso mais explícito sobre o que de fato está sendo vendido. Ele não entrou na Copa com uma animação grátis: abriu uma \u003cstrong\u003eloja\u003c/strong\u003e. A \u003cem\u003eDuo Cup\u003c/em\u003e tem uma \u0026ldquo;Loja da Duo Cup\u0026rdquo; que te convida a \u003cem\u003e\u0026ldquo;comprar os uniformes dos seus times favoritos\u0026rdquo;\u003c/em\u003e a \u003cstrong\u003eUS$ 1,99 cada\u003c/strong\u003e, com um relógio de 21 dias correndo e uma seção de \u0026ldquo;ofertas especiais: mais uniformes, mais economia\u0026rdquo; para comprar em combo. Quase toda seleção custa dinheiro; só algumas vêm de graça.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/duolingo-store_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/duolingo-store_pt.png\" data-sub-html=\"\u003ch2\u003eA Loja da Duo Cup: a camisa da sua seleção custa US$ 1,99. Argentina, França, Croácia, Espanha, México, Japão, Senegal… quase todas pagas, com contagem regressiva de 21 dias e descontos por combo. Capturas do próprio autor, junho de 2026.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/duolingo-store_pt.png\" srcset=\"/the-update-nobody-asked-for/images/duolingo-store_pt_hu83a463c5472f79e5bec100a680b698b5_256570_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/duolingo-store_pt_hu83a463c5472f79e5bec100a680b698b5_256570_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/duolingo-store_pt_hu83a463c5472f79e5bec100a680b698b5_256570_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"712\" width=\"906\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eA Loja da Duo Cup: a camisa da sua seleção custa US$ 1,99. Argentina, França, Croácia, Espanha, México, Japão, Senegal… quase todas pagas, com contagem regressiva de 21 dias e descontos por combo. Capturas do próprio autor, junho de 2026.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eO enquadramento é o que revela: o app diz \u003cem\u003e\u0026ldquo;Mostre seu apoio com o uniforme da Argentina!\u0026rdquo;\u003c/em\u003e e o único botão embaixo é \u003cstrong\u003ecomprar por US$ 1,99\u003c/strong\u003e. Se você realmente quer apoiar a sua seleção, precisa comprá-la: naquela tela, querer e pagar são o mesmo ato. Um app de idiomas transformou o senso de pertencimento a um país num item cosmético com preço, e somou uma contagem regressiva que encurta a decisão.\u003c/p\u003e\n\u003cp\u003eHavia uma opção mais interessante à mão: o app poderia atribuir a cada usuário um país e deixar você ver, nas tabelas globais, a pessoa real por trás de cada bandeira — uma imagem concreta de quão global é o evento. Em vez disso, escolheu a mecânica que converte melhor: cobrar pelo uniforme.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eO objetivo não é melhorar o software, e sim pegar carona numa onda de atenção que já existe — antes que ela se dissipe.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"não-é-o-futebol-é-cada-evento-em-cada-país\" class=\"headerLink\"\u003e\n    \u003ca href=\"#n%c3%a3o-%c3%a9-o-futebol-%c3%a9-cada-evento-em-cada-pa%c3%ads\" class=\"header-mark\" aria-label=\"Header mark for 'Não é o futebol: é cada evento, em cada país'\"\u003e\u003c/a\u003e3 Não é o futebol: é cada evento, em cada país\u003c/h2\u003e\u003cp\u003eÉ fácil olhar para tudo isso e pensar \u0026ldquo;bom, é a Copa, acontece a cada quatro anos\u0026rdquo;. Mas o futebol é só o exemplo mais visível desta semana. O mecanismo é global e não para nunca: sempre há um evento para pegar carona, em algum calendário, em alguma cultura.\u003c/p\u003e\n\u003cp\u003eNa \u003cstrong\u003eChina\u003c/strong\u003e, o \u003cem\u003eSingles Day\u003c/em\u003e (11.11) que a Alibaba inventou transformou uma data sem sentido — o 11 do 11 — no maior evento de compras do planeta: só a Alibaba faturou \u003cstrong\u003eUS$ 84,5 bilhões em 2021\u003c/strong\u003e, e junto com a JD.com passaram de \u003cstrong\u003eUS$ 139 bilhões\u003c/strong\u003e em um único dia (\u003ca href=\"https://www.cnbc.com/2021/11/12/china-singles-day-2021-alibaba-jd-hit-record-139-billion-of-sales.html\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eCNBC\u003c/a\u003e, \u003ca href=\"https://chainstoreage.com/alibaba-sets-new-singles-day-record-845-billion-sales\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eChain Store Age\u003c/a\u003e). Uma data fabricada, um app, uma urgência de um dia.\u003c/p\u003e\n\u003cp\u003eNa \u003cstrong\u003eÍndia\u003c/strong\u003e, não é futebol — é críquete. Quando a IPL joga, o app de fantasy \u003cstrong\u003eDream11\u003c/strong\u003e — que diz ter mais de \u003cstrong\u003e200 milhões de usuários\u003c/strong\u003e — chega a \u003cstrong\u003emais de 15 milhões de usuários simultâneos\u003c/strong\u003e no primeiro dia do torneio, com o críquete representando \u003cstrong\u003e87% de tudo o que se joga\u003c/strong\u003e (\u003ca href=\"https://restofworld.org/2023/dream11-fantasy-cricket-app-india/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eRest of World\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Dream11\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWikipedia\u003c/a\u003e). Um país inteiro atualizando um app no ritmo de cada over.\u003c/p\u003e\n\u003cp\u003eNo \u003cstrong\u003eOriente Médio\u003c/strong\u003e, o evento é o calendário lunar: no Ramadã, super-apps como a \u003cstrong\u003eCareem\u003c/strong\u003e reorganizam a experiência inteira em torno do iftar e do suhoor — pedidos agendados, modos especiais, funções de doação — porque o próprio dia muda de forma (\u003ca href=\"https://blog.careem.com/posts/ramadan-trends-2025\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eCareem\u003c/a\u003e, \u003ca href=\"https://www.arabianbusiness.com/culture-society/careem-shares-ramadan-2025-trends-massive-remittances-to-india-and-pakistan-most-popular-iftar-orders-top-bookings\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eArabian Business\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eBlack Friday, Ano-Novo Lunar, Halloween, o Super Bowl, Diwali: mudam o país e a desculpa, mas a mecânica é idêntica. Sempre há uma onda, e sempre há um app te esperando para você não perder. A pergunta interessante não é \u003cem\u003epor que\u003c/em\u003e fazem isso — é óbvio: converte. A pergunta é \u003cem\u003ecomo\u003c/em\u003e chegamos a um mundo em que isso é o comportamento padrão de todo software que tocamos.\u003c/p\u003e\n\u003ch2 id=\"como-isso-foi-massificado\" class=\"headerLink\"\u003e\n    \u003ca href=\"#como-isso-foi-massificado\" class=\"header-mark\" aria-label=\"Header mark for 'Como isso foi massificado'\"\u003e\u003c/a\u003e4 Como isso foi massificado\u003c/h2\u003e\u003cp\u003eNão foi acidente. O FOMO de hoje é o resultado de uma ideia de design que foi sendo lapidada durante meio século até virar o motor padrão da indústria.\u003c/p\u003e\n\u003cp\u003eComeça, antes da internet, com uma intuição econômica. Em \u003cstrong\u003e1971\u003c/strong\u003e, o Nobel \u003cstrong\u003eHerbert Simon\u003c/strong\u003e escreveu a frase que define tudo o que veio depois: \u003cem\u003e\u0026ldquo;uma riqueza de informação cria uma pobreza de atenção\u0026rdquo;\u003c/em\u003e (\u003ca href=\"https://en.wikipedia.org/wiki/Attention_economy\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eeconomia da atenção, Wikipedia\u003c/a\u003e). Se a informação é infinita e a atenção é finita, então a atenção é o recurso escasso — e onde há um recurso escasso, forma-se um mercado para explorá-lo.\u003c/p\u003e\n\u003cp\u003eEsse mercado encontrou sua mecânica na \u003cstrong\u003erecompensa variável\u003c/strong\u003e, o mesmo princípio que torna as máquinas caça-níqueis viciantes: você não sabe se ao puxar a alavanca vai ganhar algo ou nada, e essa incerteza — não o prêmio — é o que te mantém puxando. Em 2014, \u003cstrong\u003eNir Eyal\u003c/strong\u003e empacotou isso como um manual de produto em \u003cem\u003eHooked\u003c/em\u003e: gatilho, ação, recompensa variável, investimento, repetir (\u003ca href=\"https://www.thebehavioralscientist.com/articles/an-incomplete-loop-a-review-of-nir-eyals-hooked\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eresenha na The Behavioral Scientist\u003c/a\u003e). O ex-designer do Google \u003cstrong\u003eTristan Harris\u003c/strong\u003e disse de forma mais crua: seu celular é \u003cem\u003e\u0026ldquo;uma máquina caça-níquel no bolso\u0026rdquo;\u003c/em\u003e, e toda vez que você puxa para atualizar o feed, está jogando (\u003ca href=\"https://archive-yaleglobal.yale.edu/content/smartphone-addiction-slot-machine-your-pocket\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eYaleGlobal\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eE não é teoria da conspiração — quem confessou foi um dos que construíram. Em 2017, \u003cstrong\u003eSean Parker\u003c/strong\u003e, primeiro presidente do Facebook, contou que a pergunta de design era literalmente \u003cem\u003e\u0026ldquo;como consumimos a maior quantidade possível do seu tempo e da sua atenção consciente?\u0026rdquo;\u003c/em\u003e. A resposta foi te dar \u003cem\u003e\u0026ldquo;uma picadinha de dopamina\u0026rdquo;\u003c/em\u003e de vez em quando — uma curtida, um comentário — para criar um \u003cem\u003e\u0026ldquo;loop de validação social\u0026rdquo;\u003c/em\u003e que, segundo ele, \u003cem\u003e\u0026ldquo;explora uma vulnerabilidade da psicologia humana\u0026rdquo;\u003c/em\u003e. O fecho: \u003cem\u003e\u0026ldquo;Só Deus sabe o que isso está fazendo com o cérebro das nossas crianças\u0026rdquo;\u003c/em\u003e (\u003ca href=\"https://www.cnbc.com/2017/11/09/facebooks-sean-parker-on-social-media.html\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eCNBC\u003c/a\u003e, \u003ca href=\"https://www.axios.com/2017/12/15/sean-parker-facebook-was-designed-to-exploit-human-vulnerability-1513306782\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAxios\u003c/a\u003e). \u003cem\u003e\u0026ldquo;Entendíamos isso conscientemente. E fizemos mesmo assim.\u0026rdquo;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eDaí saíram as duas mecânicas que massificaram o FOMO até torná-lo invisível:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAs ofensivas (streaks) do Snapchat.\u003c/strong\u003e Talvez o mecanismo mais coercitivo do software de consumo: não só te premia por usar o app todo dia, ele te \u003cem\u003epune\u003c/em\u003e por parar. A ofensiva é um número que você construiu com um amigo e que se apaga se você falhar um dia. A pesquisa liga isso diretamente à dependência do celular e ao FOMO em adolescentes (\u003ca href=\"https://www.sciencedirect.com/science/article/pii/S2772503023000476\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eScienceDirect\u003c/a\u003e). O medo não é de perder algo bom — é de perder algo que você já tem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eO Spotify Wrapped.\u003c/strong\u003e Aqui está o paciente zero do FOMO-por-compartilhar, e é genial de tão astuto. Em 2016, o Spotify pegou os seus próprios dados — os que normalmente dão medo — e devolveu embrulhados como um presente de fim de ano, prontos para postar. Transformou vigilância em comemoração. Passou de \u003cstrong\u003e30 milhões\u003c/strong\u003e de usuários abrindo em 2017 para \u003cstrong\u003e156 milhões\u003c/strong\u003e em 2022, e em 2023 gerou \u003cstrong\u003emais de 2 bilhões de impressões\u003c/strong\u003e nas redes — publicidade grátis feita por você, porque você não queria ficar de fora do dia em que todo mundo posta seu Wrapped (\u003ca href=\"https://en.wikipedia.org/wiki/Spotify_Wrapped\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWikipedia\u003c/a\u003e, \u003ca href=\"https://theconversation.com/spotify-wrapped-success-story-unpacked-what-are-the-takeaways-251337\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eThe Conversation\u003c/a\u003e). Uma vez que um app provou que dava para fazer milhões se autopromoverem num dia fixo do ano, todos quiseram o seu próprio \u0026ldquo;Wrapped\u0026rdquo;. A Duo Cup é o Wrapped da Copa.\u003c/p\u003e\n\u003cp\u003eE, como toda mecânica levada ao extremo, tem a sua caricatura: o \u003cstrong\u003eBeReal\u003c/strong\u003e. Um app cuja \u003cem\u003eúnica\u003c/em\u003e função era o FOMO em estado puro — uma notificação em horário aleatório, dois minutos para tirar a foto, o pânico do \u0026ldquo;está na hora do BeReal\u0026rdquo;. Funcionou tão bem que \u003cstrong\u003e68% dos usuários abriam o app em até 3 minutos\u003c/strong\u003e após a notificação. E depois evaporou, do jeito que tudo evapora na economia da atenção.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/massification-decline_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/massification-decline_pt.png\" data-sub-html=\"\u003ch2\u003eA mesma curva, espelhada. O Spotify Wrapped se massificando (usuários que o abrem todo dezembro) e o BeReal esvaziando (usuários ativos mensais) após o pico de 2022. Fontes: Wikipedia (Wrapped), Business of Apps (BeReal).\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/massification-decline_pt.png\" srcset=\"/the-update-nobody-asked-for/images/massification-decline_pt_hud35250c0ea371df15dd9671a8447251e_80504_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/massification-decline_pt_hud35250c0ea371df15dd9671a8447251e_80504_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/massification-decline_pt_hud35250c0ea371df15dd9671a8447251e_80504_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"540\" width=\"1200\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eA mesma curva, espelhada. O Spotify Wrapped se massificando (usuários que o abrem todo dezembro) e o BeReal esvaziando (usuários ativos mensais) após o pico de 2022. Fontes: Wikipedia (Wrapped), Business of Apps (BeReal).\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eO BeReal passou de \u003cstrong\u003e73 milhões\u003c/strong\u003e de usuários ativos mensais em agosto de 2022 para \u003cstrong\u003e33 milhões\u003c/strong\u003e em março de 2023, e os downloads despencaram \u003cstrong\u003e60%\u003c/strong\u003e de 2023 para 2024 (\u003ca href=\"https://www.businessofapps.com/data/bereal-statistics/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eBusiness of Apps\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/BeReal\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWikipedia\u003c/a\u003e). O FOMO sobe e desce na mesma curva: a atenção que dispara como um foguete é a mesma que depois não sustenta nada. Isso vale para um app inteiro, e vale — na escala de quatro semanas — para uma bandeirinha num ícone de carro.\u003c/p\u003e\n\u003ch2 id=\"o-número-não-o-conteúdo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-n%c3%bamero-n%c3%a3o-o-conte%c3%bado\" class=\"header-mark\" aria-label=\"Header mark for 'O número, não o conteúdo'\"\u003e\u003c/a\u003e5 O número, não o conteúdo\u003c/h2\u003e\u003cp\u003eO que une tudo isso é que paramos de medir a contribuição e começamos a medir o momento. O que importa é estar lá, não o que você diz. E não são só os apps: redações sérias também correm atrás do número, não do conteúdo. Uma notícia que antes durava semanas hoje dura dias, porque o que importa não é a profundidade, e sim pegar a onda a tempo.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/newspapers-real_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/newspapers-real_pt.png\" data-sub-html=\"\u003ch2\u003eAs capas reais de sexta-feira, 12 de junho de 2026, o dia depois da abertura: 20 capas de 11 países (via kiosko.net). Do Clarín ao The New York Times, do L\u0026rsquo;Équipe ao Daily Mail, todos deram destaque na primeira página. Reproduzidas para comentário editorial.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/newspapers-real_pt.png\" srcset=\"/the-update-nobody-asked-for/images/newspapers-real_pt_hu334e6da1abdb8078c9767c45001f4d8a_2557391_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/newspapers-real_pt_hu334e6da1abdb8078c9767c45001f4d8a_2557391_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/newspapers-real_pt_hu334e6da1abdb8078c9767c45001f4d8a_2557391_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"1490\" width=\"1274\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eAs capas reais de sexta-feira, 12 de junho de 2026, o dia depois da abertura: 20 capas de 11 países (via kiosko.net). Do Clarín ao The New York Times, do L\u0026rsquo;Équipe ao Daily Mail, todos deram destaque na primeira página. Reproduzidas para comentário editorial.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eIsso é mensurável. Um estudo na \u003cem\u003eNature Communications\u003c/em\u003e, \u003ca href=\"https://www.nature.com/articles/s41467-019-09311-w\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003cem\u003e\u0026ldquo;Accelerating dynamics of collective attention\u0026rdquo;\u003c/em\u003e\u003c/a\u003e (\u003ca href=\"https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6465266/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ecópia grátis no PMC\u003c/a\u003e), descobriu que uma hashtag entre as 50 mais populares do Twitter ficava no topo cerca de \u003cstrong\u003e17,5 horas em 2013\u003c/strong\u003e e só \u003cstrong\u003e11,9 horas em 2016\u003c/strong\u003e. O mesmo padrão aparece nas bilheterias de cinema, nas citações científicas, no Google Books de cem anos atrás. A conclusão dos autores: a atenção coletiva tem um tamanho fixo, mas a gente coloca cada vez mais coisas para competir por ela, então cada tema se queima mais rápido e o próximo pisa em cima na sequência.\u003c/p\u003e\n\u003cp\u003eIsso é o FOMO em escala de civilização. O \u003cem\u003efear of missing out\u003c/em\u003e — medido desde 2013 com a \u003ca href=\"https://pmc.ncbi.nlm.nih.gov/articles/PMC10943642/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eescala de Przybylski\u003c/a\u003e, que o correlaciona com ansiedade, pior sono e uso problemático do celular — deixou de ser um problema pessoal para virar o modelo de negócio. A bandeirinha da Uber, o cupom da PedidosYa, o uniforme do Duolingo: os três apostam que você tem medo de ficar de fora. E quase sempre acertam.\u003c/p\u003e\n\u003ch2 id=\"o-custo-invisível-da-festa\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-custo-invis%c3%advel-da-festa\" class=\"header-mark\" aria-label=\"Header mark for 'O custo invisível da festa'\"\u003e\u003c/a\u003e6 O custo invisível da festa\u003c/h2\u003e\u003cp\u003eNada disso é de graça, mesmo que chegue grátis até você.\u003c/p\u003e\n\u003cp\u003eCada uma dessas \u0026ldquo;comemorações\u0026rdquo; é um deploy de verdade: builds novas para iOS e Android, configs remotas, feature flags, banners, assets, telemetria para medir quanta gente tocou na bandeirinha. Multiplique isso por cada app que entrou na Copa e por cada plataforma que mantêm em paralelo. É uma quantidade enorme de engenharia, de dados e de energia gasta em coisas que vão ser apagadas em quatro semanas.\u003c/p\u003e\n\u003cp\u003eE os dados não são abstratos. A eletricidade consumida pelos data centers do mundo vai \u003cstrong\u003emais que dobrar até 2030, para cerca de 945 TWh\u003c/strong\u003e — aproximadamente tudo o que o Japão consome em um ano — segundo a \u003ca href=\"https://www.iea.org/reports/energy-and-ai/executive-summary\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAgência Internacional de Energia\u003c/a\u003e. Nem toda essa energia é por bandeirinhas de futebol, claro. Mas cada feature efêmera, cada evento de telemetria, cada chuva de cupons em tempo real para milhões soma a uma infraestrutura que já pesa como um país inteiro. Geramos oceanos de dados para comemorar algo que vamos esquecer. E isso é só a eletricidade: o mundo já produz \u003ca href=\"https://unitar.org/about/news-stories/press/global-e-waste-monitor-2024-electronic-waste-rising-five-times-faster-documented-e-waste-recycling\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e62 milhões de toneladas de lixo eletrônico por ano\u003c/a\u003e, reciclado cinco vezes mais devagar do que cresce.\u003c/p\u003e\n\u003cp\u003eE há um custo que quase ninguém nesses escritórios considera: nem todo mundo tem dados para gastar numa bandeirinha. A GSMA calcula que \u003cstrong\u003e3,1 bilhões de pessoas — 38% do planeta — têm sinal de internet móvel em cima delas, mas não usam\u003c/strong\u003e, e uma das barreiras principais é o custo (\u003ca href=\"https://www.gsma.com/r/wp-content/uploads/2024/10/The-State-of-Mobile-Internet-Connectivity-Report-Key-Findings-2024.pdf\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGSMA, \u003cem\u003eState of Mobile Internet Connectivity 2024\u003c/em\u003e\u003c/a\u003e). A \u003ca href=\"https://a4ai.org/affordable-internet-is-1-for-2/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eAlliance for Affordable Internet\u003c/a\u003e considera \u0026ldquo;acessível\u0026rdquo; 1 GB a 2% da renda mensal ou menos; em 99 países de renda baixa e média, \u003cstrong\u003esó 31 atingem essa meta\u003c/strong\u003e, e o resto paga em média \u003cstrong\u003e5,76% do salário por 1 GB\u003c/strong\u003e. Na Argentina, onde os planos móveis custam \u003ca href=\"https://www.cronista.com/infotechnology/actualidad/argentina-tiene-uno-de-los-planes-de-celular-mas-caros-y-vale-cuatro-veces-el-precio-uruguayo/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eaté quatro vezes o que custam no Uruguai\u003c/a\u003e, racionar dados é a norma — tanto que em junho de 2026 uma empresa lançou um \u003ca href=\"https://www.canal26.com/general/2026/06/26/internet-sin-vueltas-en-la-calle-como-funciona-el-nuevo-wifi-prepago-de-telecentro-que-se-puede-usar-sin-ser-cliente/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003cem\u003e\u0026ldquo;WiFi pré-pago\u0026rdquo;\u003c/em\u003e na rua, pensado justamente para quando você fica sem dados\u003c/a\u003e. Quem vive de QR de WiFi grátis, quem deixa os dados desligados até chegar a uma rede conhecida, não é um caso raro: é metade da humanidade. Para essa pessoa, um app que se infla de animações, telemetria e assets de um evento que ela não pediu não é uma comemoração: é um pedágio. Megabytes do plano dela, queimados para uma marca entrar numa tendência.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/growth_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/growth_pt.png\" data-sub-html=\"\u003ch2\u003eTrês curvas por trás da febre: o evento (GMV do Singles Day, em US$), o dado (GB por smartphone) e o deploy (apps novos no iOS por dia). Tudo cresce; nada para. Fontes: Wikipedia, Ericsson Mobility Report 2025, 42matters.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/growth_pt.png\" srcset=\"/the-update-nobody-asked-for/images/growth_pt_hu2f60444e043eb605b47956eda5840acf_77221_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/growth_pt_hu2f60444e043eb605b47956eda5840acf_77221_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/growth_pt_hu2f60444e043eb605b47956eda5840acf_77221_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"420\" width=\"1200\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eTrês curvas por trás da febre: o evento (GMV do Singles Day, em US$), o dado (GB por smartphone) e o deploy (apps novos no iOS por dia). Tudo cresce; nada para. Fontes: Wikipedia, Ericsson Mobility Report 2025, 42matters.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003ch2 id=\"o-desfile-de-modas-que-ninguém-lembra\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-desfile-de-modas-que-ningu%c3%a9m-lembra\" class=\"header-mark\" aria-label=\"Header mark for 'O desfile de modas que ninguém lembra'\"\u003e\u003c/a\u003e7 O desfile de modas que ninguém lembra\u003c/h2\u003e\u003cp\u003eAqui está a tese grande. A Copa é um caso fácil porque tem data de início e de fim. Mas a indústria inteira funciona assim: uma moda aparece, todos pegam carona, dura uns meses, afeta gente que nem pediu, e some. E ninguém volta a falar disso.\u003c/p\u003e\n\u003cp\u003eO exemplo mais fresco é o \u003cstrong\u003eOpenClaw\u003c/strong\u003e. Lembra? Começou como um projeto pessoal chamado Clawdbot, foi renomeado para Moltbot, depois OpenClaw, e em \u003cstrong\u003efevereiro de 2026 passou de 100 mil estrelas no GitHub\u003c/strong\u003e — e em março já tinha ultrapassado o React, que rondava as 243 mil —, virando um dos repositórios não agregadores mais estrelados da história em tão pouco tempo (\u003ca href=\"https://www.kdnuggets.com/openclaw-explained-the-free-ai-agent-tool-going-viral-already-in-2026\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eKDnuggets\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/OpenClaw\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eWikipedia\u003c/a\u003e). A \u003cem\u003eFortune\u003c/em\u003e cobriu como \u003cem\u003e\u0026ldquo;a última loucura\u0026rdquo;\u003c/em\u003e, a febre do \u0026ldquo;crie uma lagosta\u0026rdquo; que transformava o setor de IA na China (\u003ca href=\"https://fortune.com/2026/03/14/openclaw-china-ai-agent-boom-open-source-lobster-craze-minimax-qwen/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eFortune\u003c/a\u003e). Enquanto isso, uma análise de segurança da Bitsight encontrou \u003cstrong\u003emais de 30 mil instâncias do OpenClaw expostas na internet\u003c/strong\u003e, muitas mal configuradas por usuários que clicaram nos avisos sem ler (\u003ca href=\"https://www.bitsight.com/blog/openclaw-ai-security-risks-exposed-instances\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eBitsight\u003c/a\u003e). Seu criador foi para a OpenAI, e o furor começou a se apagar tão rápido quanto pegou fogo.\u003c/p\u003e\n\u003cp\u003eUma loucura de meses. Gente real exposta. E quase ninguém fala mais disso.\u003c/p\u003e\n\u003cp\u003eNão é novidade. É o \u003ca href=\"https://en.wikipedia.org/wiki/Gartner_hype_cycle\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eHype Cycle da Gartner\u003c/a\u003e, descrito lá em 1995: cada tecnologia sobe a um \u0026ldquo;pico de expectativas infladas\u0026rdquo; e depois cai no \u0026ldquo;vale da desilusão\u0026rdquo; quando os experimentos não entregam. Todas percorreram:\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/hype-cycle_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/hype-cycle_pt.png\" data-sub-html=\"\u003ch2\u003eO mesmo gráfico, repetidamente: cada tecnologia sobe ao pico de expectativas infladas e cai no vale da desilusão. De cada dez que caem, seis nunca voltam. Fonte: Gartner Hype Cycle.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/hype-cycle_pt.png\" srcset=\"/the-update-nobody-asked-for/images/hype-cycle_pt_hu0c20d423337a38d9b5cc7690956ee2a9_76867_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/hype-cycle_pt_hu0c20d423337a38d9b5cc7690956ee2a9_76867_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/hype-cycle_pt_hu0c20d423337a38d9b5cc7690956ee2a9_76867_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"540\" width=\"1200\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eO mesmo gráfico, repetidamente: cada tecnologia sobe ao pico de expectativas infladas e cai no vale da desilusão. De cada dez que caem, seis nunca voltam. Fonte: Gartner Hype Cycle.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eO blockchain caiu do pico para o vale e ficou lá: a maioria das suas aplicações segue \u003ca href=\"https://www.ciodive.com/news/most-blockchain-applications-sunk-in-the-trough-of-disillusionment-gartn/564613/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eafundada no vale da desilusão\u003c/a\u003e, segundo a própria Gartner. A IoT, o big data, o serverless: cada um foi, no seu momento, o que ia mudar tudo. De cada tecnologia que cai no vale, \u003cstrong\u003eseis em cada dez nunca voltam a subir\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eE a mãe de todas as modas nem sequer foi digital: foi mudar o trabalho da América para a Ásia. Foi vendida como progresso, como eficiência, como inevitável. Entre 2001 e 2013, o déficit comercial com a China custou aos Estados Unidos, segundo a estimativa do \u003ca href=\"https://www.epi.org/publication/china-trade-outsourcing-and-jobs/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eEconomic Policy Institute\u003c/a\u003e, \u003cstrong\u003e3,2 milhões de empregos\u003c/strong\u003e, dos quais \u003cstrong\u003e2,4 milhões eram industriais\u003c/strong\u003e. Aquilo não foi uma bandeirinha que se apaga em quatro semanas; aquilo mudou cidades inteiras para sempre. Mas o mecanismo mental foi idêntico: uma ideia que todos adotam ao mesmo tempo porque ninguém quer ficar de fora, e o custo é pago por outro, em outro lugar.\u003c/p\u003e\n\u003ch2 id=\"a-moda-de-hoje-coloque-agente-em-tudo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-moda-de-hoje-coloque-agente-em-tudo\" class=\"header-mark\" aria-label=\"Header mark for 'A moda de hoje: coloque \u0026amp;ldquo;agente\u0026amp;rdquo; em tudo'\"\u003e\u003c/a\u003e8 A moda de hoje: coloque \u0026ldquo;agente\u0026rdquo; em tudo\u003c/h2\u003e\u003cp\u003eSe o OpenClaw foi o pico viral, os \u003cstrong\u003e\u0026ldquo;agentes\u0026rdquo; de IA\u003c/strong\u003e são a onda corporativa que veio atrás. Em 2025 e 2026 não houve uma grande empresa de software que não anunciasse sua plataforma de agentes. Não preciso listar cem logos — o dado agregado diz mais.\u003c/p\u003e\n\u003cp\u003eA Gartner estima que, dos \u003cstrong\u003emilhares de fornecedores\u003c/strong\u003e que dizem vender \u0026ldquo;IA agêntica\u0026rdquo;, \u003cstrong\u003esó uns 130 são reais\u003c/strong\u003e — o resto pratica o que chamam de \u003cem\u003e\u0026ldquo;agent washing\u0026rdquo;\u003c/em\u003e: rebatizar de \u0026ldquo;agente\u0026rdquo; o chatbot, o RPA ou o assistente que já tinham. E prevê que \u003cstrong\u003emais de 40% dos projetos de IA agêntica vão ser cancelados até o fim de 2027\u003c/strong\u003e, por custos que disparam, valor de negócio difuso e controles de risco insuficientes (\u003ca href=\"https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGartner, junho de 2025\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eMilhares de empresas vendendo agentes, 130 reais.\u003c/em\u003e Essa é a sua captura de cem apps, condensada num número. Mas alguns nomes grandes, para você ver a avalanche:\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eEmpresa\u003c/th\u003e\n\u003cth\u003eO que lançou\u003c/th\u003e\n\u003cth\u003eFonte\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eSalesforce\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eAgentforce — agentes para a empresa toda, vendidos como \u0026ldquo;a empresa agêntica\u0026rdquo;\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://www.salesforce.com/news/press-releases/2025/10/13/agentic-enterprise-announcement/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eSalesforce\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eMicrosoft\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eCopilot + \u0026ldquo;Agent 365\u0026rdquo;, apresentado no Ignite 2025\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://www.microsoft.com/en-us/microsoft-365/blog/2025/11/18/microsoft-ignite-2025-copilot-and-agents-built-to-power-the-frontier-firm/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eMicrosoft\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eOracle\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eNovos agentes no Fusion Applications + AI Agent Studio\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://www.oracle.com/news/announcement/ai-world-oracle-advances-enterprise-ai-with-new-agents-across-fusion-applications-2025-10-15/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eOracle\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eGoogle\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eAgentspace / Vertex AI Agent Builder\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://cloud.google.com/blog/products/ai-machine-learning/google-agentspace-enables-the-agent-driven-enterprise\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGoogle Cloud\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eServiceNow\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eInovações de IA agêntica; comprou a Moveworks\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://newsroom.servicenow.com/press-releases/details/2025/ServiceNow-announces-new-agentic-AI-innovations-to-autonomously-solve-the-most-complex-enterprise-challenges-01-29-2025-traffic/default.aspx\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eServiceNow\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eSAP\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eJoule agents + inteligência embarcada\u003c/td\u003e\n\u003ctd\u003e\u003ca href=\"https://news.sap.com/2025/10/sap-connect-business-ai-new-joule-agents-embedded-intelligence/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eSAP\u003c/a\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eO padrão é sempre o mesmo, né? Não é \u0026ldquo;tenho um problema e o resolvo melhor\u0026rdquo;. É \u0026ldquo;tem um balão inflando e quero pegar carona antes que estoure\u0026rdquo;. O avanço vira negócio, não melhoria. E quando 40% desses projetos forem cancelados em 2027, vamos estar todos olhando para o outro lado, falando da moda seguinte.\u003c/p\u003e\n\u003ch2 id=\"os-números\" class=\"headerLink\"\u003e\n    \u003ca href=\"#os-n%c3%bameros\" class=\"header-mark\" aria-label=\"Header mark for 'Os números'\"\u003e\u003c/a\u003e9 Os números\u003c/h2\u003e\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/the-update-nobody-asked-for/images/numbers-grouped_pt.png\" title=\"\" data-thumbnail=\"/the-update-nobody-asked-for/images/numbers-grouped_pt.png\" data-sub-html=\"\u003ch2\u003eOs números do artigo, agrupados nos seus três motores: os picos do evento, a máquina do FOMO e o que custa. Cada fonte está linkada ao longo do texto.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/the-update-nobody-asked-for/images/numbers-grouped_pt.png\" srcset=\"/the-update-nobody-asked-for/images/numbers-grouped_pt_hua92f56d7106599c5f601e24c90fad8ad_150248_800x0_resize_q75_h2_box_3.webp 800w, /the-update-nobody-asked-for/images/numbers-grouped_pt_hua92f56d7106599c5f601e24c90fad8ad_150248_1200x0_resize_q75_h2_box_3.webp 1200w, /the-update-nobody-asked-for/images/numbers-grouped_pt_hua92f56d7106599c5f601e24c90fad8ad_150248_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"760\" width=\"1200\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eOs números do artigo, agrupados nos seus três motores: os picos do evento, a máquina do FOMO e o que custa. Cada fonte está linkada ao longo do texto.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003ch2 id=\"amanhã-vão-esquecer\" class=\"headerLink\"\u003e\n    \u003ca href=\"#amanh%c3%a3-v%c3%a3o-esquecer\" class=\"header-mark\" aria-label=\"Header mark for 'Amanhã vão esquecer'\"\u003e\u003c/a\u003e10 Amanhã vão esquecer\u003c/h2\u003e\u003cp\u003eQuando a Copa acabar, os carrinhos vão perder a bandeirinha, os cupons vão se apagar, o Duo vai trocar de roupa e ninguém vai ligar para o uniforme que tanto custou. O app do TikTok dedicado ao torneio vai ficar morto na loja. A infraestrutura que fizemos deploy para tudo isso vai ser desmontada, e vamos começar a pensar no que vem depois. Porque lembrar custa caro.\u003c/p\u003e\n\u003cp\u003eEssa é a verdadeira função de toda essa máquina — a que Parker, Eyal e os outros projetaram, e a que o Snapchat e o Spotify aperfeiçoaram: não que você lembre, e sim que você nunca pare de olhar. Se você ficasse pensando na bandeirinha da semana passada, não estaria disponível para a moda da semana que vem. O esquecimento não é um efeito colateral; é o produto.\u003c/p\u003e\n\u003cp\u003eNão há nada de errado com uma animação de gol ou um uniforme de futebol em si. O notável é que isso tenha virado o \u003cem\u003eauge\u003c/em\u003e da ambição do software de consumo. Antes a gente esperava meses por uma atualização porque ela ia melhorar algo que usávamos todo dia. Hoje atualizamos todo dia para que você não esqueça de olhar — e, acima de tudo, para que você olhe algo que antes nunca te interessou: um jogo entre dois países que não são o seu, um resultado que não muda nada na sua vida, um evento que aconteceu a milhares de quilômetros e do qual você vai ficar sabendo de qualquer jeito porque o app fez questão de você não poder ignorar. Te ensinaram a ter FOMO de coisas que ontem você nem sabia que existiam.\u003c/p\u003e\n\u003cp\u003eDa próxima vez que um app mudar de um dia para o outro, pergunte-se uma coisa só: isso serve a mim, ou serve para eu não ir embora? Você quase sempre vai saber a resposta. E quase sempre, amanhã, você já não vai lembrar — com certeza tem algo mais urgente precisando da sua atenção, e você não pode perder.\u003c/p\u003e\n",
        "language": "pt"
    },
    {
        "title" : "Como as Transmissões Piratas da Copa do Mundo Realmente Funcionam",
        "date_published" : "2026-06-17T00:00:00Z",
        "date_modified" : "2026-06-17T00:00:00Z",
        "id" : "https://misael.org/pt/clearkey-drm-world-cup-2026/",
        "url" : "https://misael.org/pt/clearkey-drm-world-cup-2026/",
        "summary": "Fui investigar como sites de streaming pirata e apps Android realmente transmitem a Copa do Mundo. As chaves de DRM estavam no HTML, a criptografia do app era AES-ECB, e o \u0026lsquo;servidor de licenças\u0026rsquo; era uma URL com a chave na query string.",
        "content_html" : "\u003cp\u003eEu quis descobrir como aquele truque escondido à plena vista realmente funcionava. Sites de streaming pirata existem desde sempre, mas durante a Copa do Mundo você não conseguia abrir um fórum de esportes sem alguém soltando um link para uma partida ao vivo — Full HD, sem buffering, sem login. Eu sempre presumi que havia algo exótico acontecendo por baixo dos panos. Um amigo me mandou um link antes do jogo Inglaterra-Croácia. \u0026ldquo;Assiste isso\u0026rdquo;, ele disse. Então eu abri o DevTools.\u003c/p\u003e\n\u003cp\u003eEu esperava uma playlist HLS raspada ou um feed retransmitido. Em vez disso, encontrei uma tag \u003ccode\u003e\u0026lt;video\u0026gt;\u003c/code\u003e, uma instância do Shaka Player apontando para um CDN legítimo da Akamai, e duas strings hexadecimais em um bloco \u003ccode\u003e\u0026lt;script\u0026gt;\u003c/code\u003e que faziam todo o trabalho. Aquelas strings hexadecimais eram as chaves de descriptografia do DRM. No código-fonte da página. Não atrás de uma API. Não criptografadas. Apenas envoltas em JavaScript que parecia assustador e não fazia nada.\u003c/p\u003e\n\u003cp\u003eAí alguém me mostrou um app Android fazendo a mesma coisa para todas as partidas da Copa do Mundo — mas \u0026ldquo;do jeito certo\u0026rdquo;, com configs criptografadas, Firebase Remote Config, um servidor de licenças, tudo. Uma seção dedicada \u0026ldquo;MUNDIAL FIFA 2026\u0026rdquo; com 28 canais, incluindo multicam, câmera do jogador e câmera do técnico para cada jogo. Levou cerca de uma hora para perceber que era a mesma vulnerabilidade vestindo um terno mais bonito.\u003c/p\u003e\n\u003cp\u003eO que começou como trinta minutos de curiosidade virou um fim de semana inteiro. No fim, eu tinha mapeado \u003cstrong\u003e670 transmissões ao vivo em 169 CDNs em 33 países\u003c/strong\u003e pelo lado da web, e \u003cstrong\u003emais 525 canais de um único app Android\u003c/strong\u003e — todos protegidos por um esquema de DRM que a própria W3C chama de ferramenta de \u0026ldquo;teste\u0026rdquo;. Durante uma Copa do Mundo em que a Inglaterra está aplicando quatro na Croácia e cada gol está simultaneamente disponível em centenas de transmissões não autorizadas.\u003c/p\u003e\n\u003cdiv class=\"details admonition warning open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z\"/\u003e\u003c/svg\u003e\u003c/span\u003eAviso\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003e\u003cstrong\u003eEsta é uma análise de vulnerabilidade.\u003c/strong\u003e Nenhuma chave real, URL de transmissão ou ferramenta funcional é publicada. Nomes de apps e domínios estão omitidos. O objetivo é documentar uma fragilidade sistêmica na forma como as plataformas implementam DRM — não viabilizar a pirataria.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"o-cadeado-com-a-combinação-escrita-no-verso\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-cadeado-com-a-combina%c3%a7%c3%a3o-escrita-no-verso\" class=\"header-mark\" aria-label=\"Header mark for 'O Cadeado Com a Combinação Escrita no Verso'\"\u003e\u003c/a\u003e1 O Cadeado Com a Combinação Escrita no Verso\u003c/h2\u003e\u003cp\u003eTodo navegador que reproduz vídeo criptografado usa as Encrypted Media Extensions (EME) da W3C. As EME suportam quatro sistemas de chave. Três deles — Widevine, FairPlay, PlayReady — usam servidores de licença, trocas de chave criptografadas e descriptografia respaldada por hardware. O quarto é o ClearKey.\u003c/p\u003e\n\u003cp\u003eO ClearKey envia a chave de descriptografia ao navegador em texto puro.\u003c/p\u003e\n\u003cp\u003eÉ isso. A mesma criptografia AES-128 do Widevine. A mesma entrega via MPEG-DASH. Mas onde o Widevine negocia chaves por um canal seguro e descriptografa o vídeo dentro de um sandbox de hardware no qual o navegador não consegue espiar, o ClearKey entrega a chave bruta ao JavaScript e diz \u0026ldquo;aqui está\u0026rdquo;.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-1\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    WIDEVINE L1                          CLEARKEY\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ───────────────────────              ───────────────────────\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    License server (HTTPS)               No license server\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Encrypted key exchange               Key in plaintext JS\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Hardware TEE decryption              Software decryption\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Key never in JS memory               Key IS the JS\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Per-device, per-session policy       No policy at all\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO DASH Industry Forum afirma que o ClearKey é \u0026ldquo;recomendado apenas para fins de teste\u0026rdquo;. Algumas plataformas decidiram usá-lo em produção mesmo assim. Tanto os sites quanto o app que analisei dependem dessa decisão.\u003c/p\u003e\n\u003ch2 id=\"parte-1-os-sites\" class=\"headerLink\"\u003e\n    \u003ca href=\"#parte-1-os-sites\" class=\"header-mark\" aria-label=\"Header mark for 'Parte 1: Os Sites'\"\u003e\u003c/a\u003e2 Parte 1: Os Sites\u003c/h2\u003e\u003cp\u003eExaminei dezenas de sites de streaming pirata. Todos seguem o mesmo padrão.\u003c/p\u003e\n\u003ch3 id=\"a-arquitetura\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-arquitetura\" class=\"header-mark\" aria-label=\"Header mark for 'A Arquitetura'\"\u003e\u003c/a\u003e2.1 A Arquitetura\u003c/h3\u003e\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-2\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e┌──────────────────────────────────────────────────────────────┐\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e                    \u003cspan class=\"n\"\u003eHOW\u003c/span\u003e \u003cspan class=\"n\"\u003eIT\u003c/span\u003e \u003cspan class=\"n\"\u003eACTUALLY\u003c/span\u003e \u003cspan class=\"n\"\u003eWORKS\u003c/span\u003e                     \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e└──────────────────────────────────────────────────────────────┘\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eSTEP\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e                    \u003cspan class=\"n\"\u003eSTEP\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e                    \u003cspan class=\"n\"\u003eSTEP\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eUser\u003c/span\u003e \u003cspan class=\"n\"\u003evisits\u003c/span\u003e               \u003cspan class=\"n\"\u003eClicks\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"n\"\u003echannel\u003c/span\u003e          \u003cspan class=\"n\"\u003ePage\u003c/span\u003e \u003cspan class=\"n\"\u003eloads\u003c/span\u003e \u003cspan class=\"n\"\u003ean\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eiframe\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003epirate\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003esite\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eco\u003c/span\u003e            \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eg\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Sky Sports\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e       \u003cspan class=\"n\"\u003efrom\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"n\"\u003edifferent\u003c/span\u003e \u003cspan class=\"n\"\u003edomain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e┌──────────────┐\u003c/span\u003e          \u003cspan class=\"err\"\u003e┌──────────────┐\u003c/span\u003e          \u003cspan class=\"err\"\u003e┌──────────────────┐\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e              \u003cspan class=\"err\"\u003e│\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"err\"\u003e┌────┐┌────┐│\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eplayer\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003ehost\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eco\u003c/span\u003e  \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eChannel\u003c/span\u003e     \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eclick\u003c/span\u003e   \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"err\"\u003e│\u003c/span\u003e\u003cspan class=\"n\"\u003eESPN\u003c/span\u003e\u003cspan class=\"err\"\u003e││\u003c/span\u003e\u003cspan class=\"n\"\u003eDAZN\u003c/span\u003e\u003cspan class=\"err\"\u003e││\u003c/span\u003e  \u003cspan class=\"n\"\u003eiframe\u003c/span\u003e  \u003cspan class=\"err\"\u003e│\u003c/span\u003e                  \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eGrid\u003c/span\u003e \u003cspan class=\"n\"\u003ePage\u003c/span\u003e   \u003cspan class=\"err\"\u003e│\u003c/span\u003e \u003cspan class=\"err\"\u003e───────\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"err\"\u003e├────┤├────┤│\u003c/span\u003e \u003cspan class=\"err\"\u003e───────\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eShaka\u003c/span\u003e \u003cspan class=\"n\"\u003ePlayer\u003c/span\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e              \u003cspan class=\"err\"\u003e│\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"err\"\u003e│\u003c/span\u003e\u003cspan class=\"n\"\u003eSky\u003c/span\u003e \u003cspan class=\"err\"\u003e││\u003c/span\u003e\u003cspan class=\"n\"\u003ebeIN\u003c/span\u003e\u003cspan class=\"err\"\u003e││\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003eDRM\u003c/span\u003e \u003cspan class=\"n\"\u003ekeys\u003c/span\u003e      \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003estatic\u003c/span\u003e     \u003cspan class=\"err\"\u003e│\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"err\"\u003e└────┘└────┘│\u003c/span\u003e          \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003emanifest\u003c/span\u003e \u003cspan class=\"n\"\u003eURL\u003c/span\u003e  \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e│\u003c/span\u003e   \u003cspan class=\"n\"\u003eHTML\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e      \u003cspan class=\"err\"\u003e│\u003c/span\u003e          \u003cspan class=\"err\"\u003e└──────────────┘\u003c/span\u003e          \u003cspan class=\"err\"\u003e└────────┬─────────┘\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"err\"\u003e└──────────────┘\u003c/span\u003e                                             \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                                 \u003cspan class=\"err\"\u003e│\u003c/span\u003e \u003cspan class=\"n\"\u003efetches\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                                 \u003cspan class=\"err\"\u003e▼\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e┌──────────────────────┐\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eLEGITIMATE\u003c/span\u003e \u003cspan class=\"n\"\u003eCDN\u003c/span\u003e      \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eakamaized\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enet\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e     \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e   \u003cspan class=\"n\"\u003eskycdp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecom\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e        \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e   \u003cspan class=\"n\"\u003eindazn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecom\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e        \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e                      \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003eEncrypted\u003c/span\u003e \u003cspan class=\"n\"\u003eDASH\u003c/span\u003e      \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e│\u003c/span\u003e  \u003cspan class=\"n\"\u003esegments\u003c/span\u003e            \u003cspan class=\"err\"\u003e│\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                    \u003cspan class=\"err\"\u003e└──────────────────────┘\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eThe\u003c/span\u003e \u003cspan class=\"n\"\u003epirate\u003c/span\u003e \u003cspan class=\"n\"\u003esite\u003c/span\u003e \u003cspan class=\"n\"\u003eserves\u003c/span\u003e \u003cspan class=\"n\"\u003eZERO\u003c/span\u003e \u003cspan class=\"n\"\u003evideo\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eThe\u003c/span\u003e \u003cspan class=\"n\"\u003eCDN\u003c/span\u003e \u003cspan class=\"n\"\u003ebill\u003c/span\u003e \u003cspan class=\"n\"\u003ebelongs\u003c/span\u003e \u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ethe\u003c/span\u003e \u003cspan class=\"n\"\u003elegitimate\u003c/span\u003e \u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eThe\u003c/span\u003e \u003cspan class=\"n\"\u003epirate\u003c/span\u003e \u003cspan class=\"n\"\u003esite\u003c/span\u003e \u003cspan class=\"n\"\u003ehosts\u003c/span\u003e \u003cspan class=\"o\"\u003e~\u003c/span\u003e\u003cspan class=\"mi\"\u003e50\u003c/span\u003e\u003cspan class=\"n\"\u003eKB\u003c/span\u003e \u003cspan class=\"n\"\u003eof\u003c/span\u003e \u003cspan class=\"n\"\u003eHTML\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO site pirata é apenas um intermediário que conhece duas strings hexadecimais. Ele aponta o navegador do espectador para um CDN real, entrega a chave de descriptografia, e o navegador faz o resto. O CDN nunca sabe que o espectador não é um assinante pagante.\u003c/p\u003e\n\u003ch3 id=\"a-chave-está-no-código-fonte\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-chave-est%c3%a1-no-c%c3%b3digo-fonte\" class=\"header-mark\" aria-label=\"Header mark for 'A Chave Está no Código-Fonte'\"\u003e\u003c/a\u003e2.2 A Chave Está no Código-Fonte\u003c/h3\u003e\u003cp\u003eA página do host do player contém um bloco \u003ccode\u003e\u0026lt;script\u0026gt;\u003c/code\u003e com as chaves, envolto em uma ofuscação que um script Node de 30 linhas desfaz em menos de um segundo:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ejavascript\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-3\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Before: 40 lines of _0x4a2f, IIFEs, string-array lookups\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// After: this is what it actually does\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003edrmKeyId\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;a1b2c3d4e5f6a7b80000000000000000\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003edrmKey\u003c/span\u003e   \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;0123456789abcdef0123456789abcdef\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eplayer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003econfigure\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003edrm\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eclearKeys\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edrmKeyId\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003edrmKey\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eplayer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eload\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;https://cdn.legitimate-provider.com/manifest.mpd\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDuas variáveis. Duas strings hexadecimais. Essa é toda a \u0026ldquo;proteção DRM\u0026rdquo;. A ofuscação — renomeação de variáveis, arrays de strings, achatamento de fluxo de controle — é desfeita pelo \u003ccode\u003eeval()\u003c/code\u003e, porque o navegador também tem que executá-la. Se o navegador consegue executar, um script consegue executar.\u003c/p\u003e\n\u003cp\u003eUma vez extraídas, as chaves se espalham por playlists M3U no GitHub e no Telegram. Encontrei um único arquivo M3U com \u003cstrong\u003e545 entradas ClearKey em 92 CDNs\u003c/strong\u003e. Público, pesquisável, indexado pelo Google. Tempo da extração até a distribuição global: minutos. Tempo para a chave ser revogada: geralmente nunca.\u003c/p\u003e\n\u003ch2 id=\"parte-2-o-app-android\" class=\"headerLink\"\u003e\n    \u003ca href=\"#parte-2-o-app-android\" class=\"header-mark\" aria-label=\"Header mark for 'Parte 2: O App Android'\"\u003e\u003c/a\u003e3 Parte 2: O App Android\u003c/h2\u003e\u003cp\u003eNem toda operação pirata é uma página web estática. Algumas distribuem um app Android completo — com telas de login, analytics do Firebase, configs criptografadas e uma UI profissional. Um app que analisei durante a Copa do Mundo segue essa abordagem. 525 canais. 36 categorias. Uma seção dedicada \u0026ldquo;MUNDIAL FIFA 2026\u0026rdquo; com ângulos multicam para cada partida.\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/clearkey-drm-world-cup-2026/images/app-config.png\" title=\"\" data-thumbnail=\"/clearkey-drm-world-cup-2026/images/app-config.png\" data-sub-html=\"\u003ch2\u003eA config criptografada de canais do app — 525 canais em 36 categorias, com URLs criptografadas em AES e credenciais de DRM. [URLs omitidas]\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/clearkey-drm-world-cup-2026/images/app-config.png\" srcset=\"/clearkey-drm-world-cup-2026/images/app-config_hua7b6385fc770026e215313f327bfe973_145312_800x0_resize_q75_h2_box_3.webp 800w, /clearkey-drm-world-cup-2026/images/app-config_hua7b6385fc770026e215313f327bfe973_145312_1200x0_resize_q75_h2_box_3.webp 1200w, /clearkey-drm-world-cup-2026/images/app-config_hua7b6385fc770026e215313f327bfe973_145312_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"1132\" width=\"1621\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eA config criptografada de canais do app — 525 canais em 36 categorias, com URLs criptografadas em AES e credenciais de DRM. [URLs omitidas]\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003ch3 id=\"a-criptografia-aes-128-ecb\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-criptografia-aes-128-ecb\" class=\"header-mark\" aria-label=\"Header mark for 'A Criptografia (AES-128-ECB)'\"\u003e\u003c/a\u003e3.1 A Criptografia (AES-128-ECB)\u003c/h3\u003e\u003cp\u003eO app busca sua lista de canais como um JSON criptografado em AES de um servidor remoto. Cada campo — URLs de transmissão, URIs de licença de DRM, cabeçalhos HTTP — é texto cifrado AES codificado em base64. A chave de descriptografia é uma string de 16 caracteres armazenada no Firebase Remote Config, buscada na inicialização.\u003c/p\u003e\n\u003cp\u003eParece sério. Aí você olha o texto cifrado:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-4\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    236 URLs share the same first 35 encrypted blocks.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Block 0: f91b7f273abccc6e...  ← identical across all 236\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Block 1: 932b23560f1d43ba...  ← identical\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Block 2: a7d788a399cea962...  ← identical\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ECB mode. Same plaintext block = same ciphertext block.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    The URLs all start with the same CDN domain prefix.\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO AES-ECB é o exemplo clássico de como \u003cem\u003enão\u003c/em\u003e usar AES. Todo curso de criptografia ensina isso com o \u003ca href=\"https://blog.filippo.io/the-ecb-penguin/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003epinguim do ECB\u003c/a\u003e — criptografe um bitmap com ECB e a imagem ainda é reconhecível porque blocos de texto puro idênticos produzem blocos de texto cifrado idênticos. Este app criptografa 236 URLs que começam com o mesmo domínio de CDN, produzindo 236 textos cifrados com um \u003cstrong\u003eprefixo idêntico de 560 bytes\u003c/strong\u003e. A análise de padrões por si só revela a estrutura antes mesmo de você encontrar a chave.\u003c/p\u003e\n\u003ch3 id=\"o-servidor-de-licenças-que-não-é\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-servidor-de-licen%c3%a7as-que-n%c3%a3o-%c3%a9\" class=\"header-mark\" aria-label=\"Header mark for 'O \u0026amp;ldquo;Servidor de Licenças\u0026amp;rdquo; Que Não É'\"\u003e\u003c/a\u003e3.2 O \u0026ldquo;Servidor de Licenças\u0026rdquo; Que Não É\u003c/h3\u003e\u003cp\u003eUma vez descriptografadas, as credenciais ClearKey não estão em uma troca de chaves separada. Elas estão na \u003cstrong\u003eprópria URI de licença\u003c/strong\u003e, como parâmetros de query em texto puro:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-5\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    drm_license_uri (after decryption):\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    https://[redacted]/?keyid=49eb924b...\u0026amp;key=6e131b04...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                              ^^^^^^^^        ^^^^^^^^\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                              ClearKey ID     ClearKey Key\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                              (in the URL)    (in the URL)\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO \u0026ldquo;servidor de licenças\u0026rdquo; do app é uma URL que \u003cem\u003econtém a chave\u003c/em\u003e. Não há desafio-resposta. Nenhuma vinculação de sessão. O app busca a URL, a URL \u003cem\u003eé\u003c/em\u003e a chave, e a chave descriptografa a transmissão.\u003c/p\u003e\n\u003cp\u003eTrês camadas de indireção — Firebase Remote Config, criptografia AES, um endpoint de \u0026ldquo;servidor de licenças\u0026rdquo; — todas colapsando na mesma falha: a chave de descriptografia acaba no cliente, em texto puro, porque o ClearKey exige isso.\u003c/p\u003e\n\u003ch3 id=\"a-stack-completa-do-app\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-stack-completa-do-app\" class=\"header-mark\" aria-label=\"Header mark for 'A Stack Completa do App'\"\u003e\u003c/a\u003e3.3 A Stack Completa do App\u003c/h3\u003e\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-6\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ┌──────────────────────────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    │           ANDROID APP ARCHITECTURE                           │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    └──────────────────────────────────────────────────────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    1. App launches\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       └──\u0026gt; Firebase Remote Config\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            └──\u0026gt; Fetches AES key (\u0026#34;claveapp\u0026#34;) + config URLs\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    2. App fetches encrypted JSON (525 channels)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       └──\u0026gt; Base64 decode ──\u0026gt; AES-128-ECB decrypt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            └──\u0026gt; Stream URLs, DRM license URIs, headers (plaintext)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    3. For CLEARKEY channels (348 of 525):\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       └──\u0026gt; \u0026#34;License URI\u0026#34; = https://[redacted]/?keyid=XXX\u0026amp;key=YYY\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            └──\u0026gt; Key ID and Key are IN THE URL\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    4. App configures ExoPlayer with ClearKey\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       └──\u0026gt; Fetches DASH manifest from legitimate CDN\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            └──\u0026gt; Decrypts video with the key from step 3\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Package name: com.example.myapplication\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Encryption:   AES/ECB/PKCS5Padding (textbook insecure)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Key storage:  Firebase Remote Config (single API call to extract)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Key length:   16 characters, static, never rotated\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO nome do pacote \u003ccode\u003ecom.example.myapplication\u003c/code\u003e diz tudo sobre o rigor de desenvolvimento por trás dessa operação. O template padrão do Android Studio. Nem renomeado.\u003c/p\u003e\n\u003ch3 id=\"a-lista-de-canais-ao-vivo-mora-em-um-repo-público\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-lista-de-canais-ao-vivo-mora-em-um-repo-p%c3%bablico\" class=\"header-mark\" aria-label=\"Header mark for 'A Lista de Canais ao Vivo Mora em um Repo Público'\"\u003e\u003c/a\u003e3.4 A Lista de Canais ao Vivo Mora em um Repo Público\u003c/h3\u003e\u003cp\u003eA parte mais silenciosamente condenatória — e a parte que mostra a \u003cem\u003ereal\u003c/em\u003e sofisticação da operação, que é majoritariamente logística — é onde a config criptografada está hospedada. O app busca seu JSON de canais em um repositório público do GitHub. Qualquer um pode navegá-lo. Qualquer um pode dar \u003ccode\u003egit clone\u003c/code\u003e nele. Qualquer um pode acompanhar o histórico de commits.\u003c/p\u003e\n\u003cp\u003eE o histórico de commits é a parte interessante:\u003c/p\u003e\n\u003cfigure\u003e\u003ca class=\"lightgallery\" href=\"/clearkey-drm-world-cup-2026/images/commits.png\" title=\"\" data-thumbnail=\"/clearkey-drm-world-cup-2026/images/commits.png\" data-sub-html=\"\u003ch2\u003eO repositório público do GitHub que hospeda a lista de canais criptografada do app. Um commit aterrissa a cada 4–5 minutos — edições automatizadas no JSON de canais conforme as transmissões upstream são rotacionadas, derrubadas ou substituídas. Nome de usuário e dono do repo omitidos.\u003c/h2\u003e\"\u003e\u003cimg  loading=\"lazy\" src=\"/clearkey-drm-world-cup-2026/images/commits.png\" srcset=\"/clearkey-drm-world-cup-2026/images/commits_hu2add8a55510f0a520df6ce349c817777_98640_800x0_resize_q75_h2_box_3.webp 800w, /clearkey-drm-world-cup-2026/images/commits_hu2add8a55510f0a520df6ce349c817777_98640_1200x0_resize_q75_h2_box_3.webp 1200w, /clearkey-drm-world-cup-2026/images/commits_hu2add8a55510f0a520df6ce349c817777_98640_1600x0_resize_q75_h2_box_3.webp 1600w\"   height=\"900\" width=\"1300\"\u003e\u003c/a\u003e\u003cfigcaption class=\"image-caption\"\u003eO repositório público do GitHub que hospeda a lista de canais criptografada do app. Um commit aterrissa a cada 4–5 minutos — edições automatizadas no JSON de canais conforme as transmissões upstream são rotacionadas, derrubadas ou substituídas. Nome de usuário e dono do repo omitidos.\u003c/figcaption\u003e\n    \u003c/figure\u003e\n\u003cp\u003eUm commit aterrissa a cada 4–5 minutos. O diff é sempre contra \u003ccode\u003etv (10).json\u003c/code\u003e ou \u003ccode\u003ebearer.json\u003c/code\u003e — a lista de canais criptografada, e as credenciais de autenticação usadas para buscar novas transmissões do upstream. As mensagens de commit são todas \u003ccode\u003e\u0026quot;Actualizar tv (10).json desde tv.json\u0026quot;\u003c/code\u003e ou \u003ccode\u003e\u0026quot;Actualizar bearer.json desde panel - \u0026lt;timestamp\u0026gt;\u0026quot;\u003c/code\u003e. Tradução: um job automatizado, em algum lugar, está reescrevendo a config de canais a cada poucos minutos e empurrando a atualização para que os celulares rodando o app peguem novas URLs de transmissão e credenciais de DRM rotacionadas dentro de um intervalo de polling.\u003c/p\u003e\n\u003cp\u003eEste é o verdadeiro fosso da operação. Não o AES-ECB (quebrado). Não o Firebase Remote Config (uma chamada de API). Não o \u0026ldquo;servidor de licenças\u0026rdquo; (uma URL com a chave dentro). O fosso é \u003cstrong\u003eoperacional\u003c/strong\u003e: uma peça de automação que mantém o JSON de canais alinhado com quaisquer feeds upstream que estejam vivos em qualquer minuto dado, hospedada à plena vista em um CDN gratuito que eles não precisaram construir. Quando um provedor legítimo rotaciona uma chave, o bot percebe, busca a nova, recriptografa a config e faz o commit. Quando um CDN bloqueia o IP de origem, o bot encontra outro. Os celulares dos clientes fazem polling, veem o novo commit, buscam a nova config e continuam assistindo à partida. O usuário nem vê a rotação acontecer.\u003c/p\u003e\n\u003cp\u003eA defesa que todo mundo recorre é \u0026ldquo;revogar a chave\u0026rdquo;. Mas você não consegue revogar uma chave mais rápido do que um script consegue empurrar uma nova para um repositório do GitHub. O descompasso na cadência é o jogo inteiro.\u003c/p\u003e\n\u003ch2 id=\"os-números\" class=\"headerLink\"\u003e\n    \u003ca href=\"#os-n%c3%bameros\" class=\"header-mark\" aria-label=\"Header mark for 'Os Números'\"\u003e\u003c/a\u003e4 Os Números\u003c/h2\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\u003c/th\u003e\n\u003cth\u003eAuditoria web\u003c/th\u003e\n\u003cth\u003eApp Android\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eTransmissões\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003e670\u003c/td\u003e\n\u003ctd\u003e525 (348 ClearKey)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eCDNs\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003e169\u003c/td\u003e\n\u003ctd\u003e34\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003ePaíses\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003e33\u003c/td\u003e\n\u003ctd\u003e~8 (foco na América Latina)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eArmazenamento da chave\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eBloco \u003ccode\u003e\u0026lt;script\u0026gt;\u003c/code\u003e JavaScript\u003c/td\u003e\n\u003ctd\u003eFirebase Remote Config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eOfuscação\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eRenomeação de variáveis JS + IIFEs\u003c/td\u003e\n\u003ctd\u003eAES-128-ECB (quebrado)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eChave em texto puro no cliente\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eSim\u003c/td\u003e\n\u003ctd\u003eSim\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eCobertura da Copa do Mundo\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eDezenas de canais de esportes\u003c/td\u003e\n\u003ctd\u003e28 canais dedicados + multicam\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eAmbas as abordagens — o site estático e o app Android completo — terminam no mesmo lugar: um par de chaves ClearKey na memória do cliente, sem mecanismo para impedir a extração, sem vinculação de sessão, sem revogação e sem rotação de chaves.\u003c/p\u003e\n\u003ch2 id=\"o-dinheiro-e-por-que-não-vai-parar\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-dinheiro-e-por-que-n%c3%a3o-vai-parar\" class=\"header-mark\" aria-label=\"Header mark for 'O Dinheiro, e Por Que Não Vai Parar'\"\u003e\u003c/a\u003e5 O Dinheiro, e Por Que Não Vai Parar\u003c/h2\u003e\u003cp\u003eA economia explica tudo. Sites piratas hospedam ~50KB de HTML estático e apontam seu navegador para o CDN de outra pessoa. O app é um wrapper fino de ExoPlayer em torno da infraestrutura de outra pessoa. Nenhum serve vídeo. Ambos monetizam através de redes de anúncios.\u003c/p\u003e\n\u003cp\u003eO StreamEast teve 1,6 bilhão de visitas antes de ser fechado em setembro de 2025. A rede de IPTV da \u0026ldquo;Operação Takendown\u0026rdquo; tinha 22 milhões de usuários e EUR 250 milhões por mês. Um site de médio porte durante a Copa do Mundo — um milhão de visitantes no dia do jogo, seis impressões de anúncio cada, CPM de US$ 1,50 — fatura \u003cstrong\u003eUS$ 9.000 por dia\u003c/strong\u003e contra custos de hospedagem essencialmente nulos.\u003c/p\u003e\n\u003cp\u003eA fiscalização não para de escalar — penas de prisão na Espanha, sentenças de US$ 18,75 milhões no Texas, 27.000 feeds derrubados na semana anterior à Copa do Mundo — e novos sites continuam aparecendo. Porque a vulnerabilidade é arquitetural. Você não consegue prender o caminho para fora de uma especificação que coloca a chave de descriptografia no navegador. Você também não consegue criptografar o caminho para fora dela, como o app Android demonstra: três camadas de criptografia, e a chave ainda acaba em texto puro no cliente, porque o ClearKey exige isso.\u003c/p\u003e\n\u003cp\u003eA correção é migrar inteiramente para fora do ClearKey — para Widevine, FairPlay ou PlayReady. Até lá, a combinação está escrita no verso do cadeado, e a Copa do Mundo está garantindo que todo mundo saiba onde olhar.\u003c/p\u003e\n",
        "language": "pt"
    },
    {
        "title" : "fastfn Parte 2: Quando uma Função Não Basta (Serviços, Workloads e o docker-compose Que Eu Não Queria Escrever)",
        "date_published" : "2026-06-09T00:00:00Z",
        "date_modified" : "2026-06-09T00:00:00Z",
        "id" : "https://misael.org/pt/fastfn-services-when-functions-arent-enough/",
        "url" : "https://misael.org/pt/fastfn-services-when-functions-arent-enough/",
        "summary": "Funções são uma ótima forma para requisição/resposta. São uma forma terrível para um banco de dados. Esta é a parte do fastfn em que serviços de longa duração se juntaram ao gateway — docker nativo, processo nativo, microVM Firecracker — tudo por trás de uma única configuração de workload.",
        "content_html" : "\u003cdiv class=\"details admonition info open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"/\u003e\u003c/svg\u003e\u003c/span\u003eParte de uma série\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003eEsta é a Parte 2 do relato sobre o fastfn. A Parte 1 — \u003ca href=\"https://misael.org/pt/fastfn-lua-to-our-lives/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003cstrong\u003efastfn Parte 1: Eu Tinha um Problema (e Apresentei Lua à Minha Vida)\u003c/strong\u003e\u003c/a\u003e — cobre o lado das funções: o gateway em Lua, o protocolo de fio JSON, os runtimes poliglotas. Este post continua de onde a Parte 1 termina e percorre serviços, workloads e o caminho do microVM Firecracker.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 id=\"capítulo-1-a-dor-ou-por-que-uma-função-não-pode-ser-um-postgres\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-1-a-dor-ou-por-que-uma-fun%c3%a7%c3%a3o-n%c3%a3o-pode-ser-um-postgres\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 1: A Dor (ou, Por Que uma Função Não Pode Ser um Postgres)'\"\u003e\u003c/a\u003e1 Capítulo 1: A Dor (ou, Por Que uma Função Não Pode Ser um Postgres)\u003c/h2\u003e\u003cp\u003eNo fim da Parte 1, o fastfn era uma coisinha feliz. Você colocava \u003ccode\u003eget.users.py\u003c/code\u003e no disco, o gateway o mapeava para uma URL, um daemon Python o pegava, e você tinha um endpoint HTTP sem encostar num Dockerfile. Era elegante do jeito que as coisas são elegantes quando resolvem só metade do seu problema.\u003c/p\u003e\n\u003cp\u003ePorque então eu tentei construir um app de verdade com ele.\u003c/p\u003e\n\u003cp\u003eE no momento em que você tenta construir um app de verdade, suas primitivas de requisição/resposta deixam de bastar. Você precisa de estado. Precisa de um banco de dados. Precisa do painel de administração que seu colega já escreveu como um app Next.js que espera ser \u003ccode\u003enext start\u003c/code\u003e na porta 3000, não uma lambda sem estado que sobe, computa e morre. Você precisa de um Redis para sessões, ou um MinIO para blobs, ou — no meu caso — um app Flask totalmente formado que nunca caberia pelo buraco de agulha do \u0026ldquo;um handler por arquivo.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eFiquei encarando isso por um tempo. Então eu disse aquilo que vinha me esforçando muito para não dizer:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eUma função é uma ótima forma para requisição/resposta. É uma forma terrível para um Postgres.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eO que me fez perceber algo desconfortável. Para todo projeto paralelo que eu já tinha rodado, a resposta para essa dor tinha sido o mesmo arquivo: \u003ccode\u003edocker-compose.yml\u003c/code\u003e. Um serviço é o meu app, o outro é um banco de dados, talvez haja um sidecar, e todos conversam numa rede implícita que o compose conjurou do nada. É uma peça linda de ergonomia, e eu a uso o tempo todo. Mas se o fastfn fosse ser minha plataforma local, e eu fosse rodar \u003ccode\u003edocker-compose up\u003c/code\u003e ao lado de \u003ccode\u003efastfn dev\u003c/code\u003e, eu teria dois espaços de URL, dois modelos de saúde, duas configurações de CORS, duas superfícies de autenticação. A razão inteira de o gateway existir era ter \u003cstrong\u003eum\u003c/strong\u003e lugar que soubesse do meu HTTP. Um \u003ccode\u003ecompose\u003c/code\u003e paralelo teria desfeito isso.\u003c/p\u003e\n\u003cp\u003eEntão eu tinha uma escolha. Ou aceito que o fastfn cuida de uma fatia do meu app e outra coisa cuida do resto. Ou estendo o fastfn para que as formas de longa duração — o Postgres, o Next.js, o Flask — vivam por trás do mesmo gateway que as funções. Mesmo \u003ccode\u003efastfn.json\u003c/code\u003e, mesmo endpoint de saúde, mesmo CORS, mesma autenticação, uma superfície HTTP unificada.\u003c/p\u003e\n\u003cp\u003eEu não me propus a escrever docker-compose. Este post é sobre como eu escrevi metade do docker-compose de propósito.\u003c/p\u003e\n\u003cp\u003eO commit do qual este post trata principalmente é \u003ccode\u003e6a54c11\u003c/code\u003e na branch \u003ccode\u003efirecracker-simple-images\u003c/code\u003e: \u003cem\u003e\u0026ldquo;Add simple native image apps and services\u0026rdquo;\u003c/em\u003e, 31 mar 2026, 2254 inserções em 26 arquivos. É ali que a ideia de um \u003cstrong\u003eworkload\u003c/strong\u003e ganhou um lar de primeira classe no fastfn. Tudo depois disso — a rede de pares por vsock em \u003ccode\u003e5568b6c\u003c/code\u003e, os padrões keep-hot em \u003ccode\u003e6fd5fec\u003c/code\u003e, a matriz de firewall + benchmark em \u003ccode\u003efd8a6b5\u003c/code\u003e — é uma evolução da mesma abstração.\u003c/p\u003e\n\u003ch2 id=\"capítulo-2-a-palavra-workload\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-2-a-palavra-workload\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 2: A Palavra \u0026amp;ldquo;Workload\u0026amp;rdquo;'\"\u003e\u003c/a\u003e2 Capítulo 2: A Palavra \u0026ldquo;Workload\u0026rdquo;\u003c/h2\u003e\u003cp\u003eA palavra da era CGI para o que eu queria era \u0026ldquo;daemon.\u0026rdquo; A palavra do systemd é \u0026ldquo;unit.\u0026rdquo; As palavras do Kubernetes são \u0026ldquo;Deployment\u0026rdquo; e \u0026ldquo;StatefulSet.\u0026rdquo; A palavra do Heroku é \u0026ldquo;entrada de Procfile.\u0026rdquo; A palavra do docker-compose é, confusamente, \u0026ldquo;service.\u0026rdquo; Eu escolhi a palavra \u003cstrong\u003eworkload\u003c/strong\u003e como guarda-chuva, e então a dividi em duas.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-1\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   fastfn.json\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   ┌──────────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │                                              │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │  functions-dir: \u0026#34;functions\u0026#34;                  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │                                              │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │  apps:                ← public HTTP faces    │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │    admin: { ... routes: [\u0026#34;/admin/*\u0026#34;] }       │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │                                              │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │  services:            ← private, injected    │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │    mysql: { port: 3306, volume: \u0026#34;mysql-data\u0026#34; }│\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │                                              │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   └──────────────────────────────────────────────┘\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA distinção é pequena mas estruturalmente essencial. Um \u003cstrong\u003eapp\u003c/strong\u003e é um workload que tem uma face pública: ele declara \u003ccode\u003eroutes\u003c/code\u003e, e o gateway expõe essas rotas para o mundo externo. Um \u003cstrong\u003eservice\u003c/strong\u003e é um workload que permanece privado: outros workloads e funções podem alcançá-lo, mas o mundo externo não. Um Postgres é um service. Um painel de administração Next.js é um app. No extremo, a única diferença entre eles é se o campo routes está definido e se o gateway os anuncia.\u003c/p\u003e\n\u003cp\u003eAmbos vivem no mesmo arquivo, e ambos são validados pelo mesmo caminho de código (\u003ccode\u003ecli/internal/workloads/config.go:12-14\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-2\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/config.go:12-15\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003eConfig\u003c/span\u003e \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"nx\"\u003eApps\u003c/span\u003e     \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eAppSpec\u003c/span\u003e     \u003cspan class=\"s\"\u003e`json:\u0026#34;apps,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"nx\"\u003eServices\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eServiceSpec\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;services,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDuas listas irmãs. Não dois mundos. O mesmo normalizador percorre ambas, o mesmo escritor de estado persiste ambas, o mesmo gateway Lua lê ambas do mesmo arquivo JSON em tempo de requisição.\u003c/p\u003e\n\u003cp\u003eIsso me fez perceber algo quase trivial mas que vale dizer: uma vez que você tem um gateway que sabe rotear, dar a ele mais um tipo de alvo não é um projeto novo. É uma nova linha numa tabela.\u003c/p\u003e\n\u003ch2 id=\"capítulo-3-a-forma-de-um-workload\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-3-a-forma-de-um-workload\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 3: A Forma de um Workload'\"\u003e\u003c/a\u003e3 Capítulo 3: A Forma de um Workload\u003c/h2\u003e\u003cp\u003eVamos olhar a forma de verdade. Aqui está a configuração mínima do fastfn com um app e um service, retirada do diff do README naquele commit (\u003ccode\u003eREADME.md:178-202\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ejson\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-3\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// fastfn.json\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;functions-dir\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;functions\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;public-base-url\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;https://api.example.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;openapi-include-internal\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;apps\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;image\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ghcr.io/acme/admin:latest\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;port\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e3000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;routes\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/admin/*\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;services\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;mysql\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;image\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mysql:8.4\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;port\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e3306\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nt\"\u003e\u0026#34;volume\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;mysql-data\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eTrês coisas merecem atenção. Primeiro, a origem da imagem. Segundo, a porta. Terceiro, as rotas (só em apps). Todo o resto é um ajuste opcional.\u003c/p\u003e\n\u003cp\u003eA origem da imagem é interessante porque há três formas válidas de declará-la, e o código de configuração impõe que você escolha exatamente uma delas (\u003ccode\u003ecli/internal/workloads/config.go:383-395\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-4\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/config.go:387-395\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003esourceCount\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"nx\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"k\"\u003erange\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eimageFile\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003edockerfile\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003estrings\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eTrimSpace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003esourceCount\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003esourceCount\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eErrorf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s\"\u003e\u0026#34;must set exactly one image source among image, image_file or dockerfile\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEntão suas três escolhas são:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eimage\u003c/code\u003e: uma referência de registro como \u003ccode\u003emysql:8.4\u003c/code\u003e, ou um caminho para um diretório de bundle Firecracker local no disco.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eimage_file\u003c/code\u003e: um arquivo OCI ou Docker local, convertido em um bundle Firecracker em cache no primeiro uso.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edockerfile\u003c/code\u003e: um caminho para um Dockerfile que o fastfn vai construir através da API do Docker Engine, e depois converter da mesma forma.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eA regra do exatamente-um é estruturalmente essencial. Ela diz \u0026ldquo;um workload, uma fonte da verdade.\u0026rdquo; Você não pode misturar um bundle local com uma imagem de registro e ver qual ganha. Por esse caminho mora uma depuração que você não quer.\u003c/p\u003e\n\u003cp\u003eA porta é mais simples: é a porta do contêiner em que o workload escuta, com uma validação de que ela cai dentro de \u003ccode\u003e1..65535\u003c/code\u003e. As rotas, para apps, são um array de prefixos de URL. A forma padrão de uma rota suporta tanto correspondência exata (\u003ccode\u003e/admin\u003c/code\u003e) quanto com glob no fim (\u003ccode\u003e/admin/*\u003c/code\u003e). Há um normalizador que apara barras finais e força uma barra inicial (\u003ccode\u003ecli/internal/workloads/config.go:941-953\u003c/code\u003e), de modo que as três formas em que você poderia escrever a rota se tornam todas uma única string canônica.\u003c/p\u003e\n\u003cp\u003eA AppSpec completa é maior que esse mínimo, porque uma vez que você começa a rodar apps de verdade você quer ajustes. Aqui está toda a superfície (\u003ccode\u003ecli/internal/workloads/config.go:66-88\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-5\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/config.go:66-88\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003eAppSpec\u003c/span\u003e \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eName\u003c/span\u003e          \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;name\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eScopeDir\u003c/span\u003e      \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;scope_dir,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eImage\u003c/span\u003e         \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;image,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eImageFile\u003c/span\u003e     \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;image_file,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eDockerfile\u003c/span\u003e    \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;dockerfile,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eContext\u003c/span\u003e       \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;context,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ePort\u003c/span\u003e          \u003cspan class=\"kt\"\u003eint\u003c/span\u003e                \u003cspan class=\"s\"\u003e`json:\u0026#34;port\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eEnv\u003c/span\u003e           \u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e  \u003cspan class=\"s\"\u003e`json:\u0026#34;env,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eCommand\u003c/span\u003e       \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e           \u003cspan class=\"s\"\u003e`json:\u0026#34;command,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eWorkingDir\u003c/span\u003e    \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;working_dir,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eUser\u003c/span\u003e          \u003cspan class=\"kt\"\u003estring\u003c/span\u003e             \u003cspan class=\"s\"\u003e`json:\u0026#34;user,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eVolume\u003c/span\u003e        \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eVolumeSpec\u003c/span\u003e        \u003cspan class=\"s\"\u003e`json:\u0026#34;volume,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eVolumes\u003c/span\u003e       \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eVolumeSpec\u003c/span\u003e      \u003cspan class=\"s\"\u003e`json:\u0026#34;volumes,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eHealthcheck\u003c/span\u003e   \u003cspan class=\"nx\"\u003eHealthcheckSpec\u003c/span\u003e    \u003cspan class=\"s\"\u003e`json:\u0026#34;healthcheck,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eRoutes\u003c/span\u003e        \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e           \u003cspan class=\"s\"\u003e`json:\u0026#34;routes,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eReplicas\u003c/span\u003e      \u003cspan class=\"kt\"\u003eint\u003c/span\u003e                \u003cspan class=\"s\"\u003e`json:\u0026#34;replicas,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ePorts\u003c/span\u003e         \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003ePortSpec\u003c/span\u003e         \u003cspan class=\"s\"\u003e`json:\u0026#34;ports,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eAccess\u003c/span\u003e        \u003cspan class=\"nx\"\u003eAccessSpec\u003c/span\u003e         \u003cspan class=\"s\"\u003e`json:\u0026#34;access,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eProcessGroups\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eProcessGroupSpec\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;process_groups,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eHA\u003c/span\u003e            \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eHAConfig\u003c/span\u003e          \u003cspan class=\"s\"\u003e`json:\u0026#34;ha,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eLifecycle\u003c/span\u003e     \u003cspan class=\"nx\"\u003eLifecycleSpec\u003c/span\u003e      \u003cspan class=\"s\"\u003e`json:\u0026#34;lifecycle,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003eServiceSpec\u003c/code\u003e é quase idêntica (\u003ccode\u003econfig.go:90-111\u003c/code\u003e), menos \u003ccode\u003eReplicas\u003c/code\u003e; para services o process group faz a contagem de réplicas no lugar. Fiquei tentado, por um instante, a unificar as duas numa só struct com um \u003ccode\u003eIsPublic bool\u003c/code\u003e. Estou feliz por não ter feito. As duas formas têm validadores sutilmente diferentes e padrões sutilmente diferentes, e tentar dobrá-las num só tipo continuava produzindo ternários onde um segundo tipo produzia clareza.\u003c/p\u003e\n\u003cp\u003eA simetria esconde uma assimetria sutil que aparece em \u003ccode\u003edefaultAppLifecycle\u003c/code\u003e vs \u003ccode\u003edefaultServiceLifecycle\u003c/code\u003e (\u003ccode\u003econfig.go:658-672\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-6\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/config.go:658-672\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003edefaultAppLifecycle\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"nx\"\u003eLifecycleSpec\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003eLifecycleSpec\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003eIdleAction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e   \u003cspan class=\"s\"\u003e\u0026#34;run\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003ePauseAfterMS\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e15000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003ePrewarm\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e      \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003edefaultServiceLifecycle\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"nx\"\u003eLifecycleSpec\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003eLifecycleSpec\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003eIdleAction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e   \u003cspan class=\"s\"\u003e\u0026#34;run\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003ePauseAfterMS\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003ePrewarm\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e      \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eApps ganham um temporizador de ociosidade de 15 segundos que podem usar se algum dia optarem por \u003ccode\u003epause\u003c/code\u003e. Services ganham zero — porque você não pausa um Postgres. Um Postgres pausado se chama \u0026ldquo;uma indisponibilidade.\u0026rdquo; A política padrão em todo lugar é \u003cem\u003econtinuar rodando, continuar quente, e pré-aquecer no boot\u003c/em\u003e. Dois modelos de ciclo de vida, um arquivo de configuração, zero arrependimentos.\u003c/p\u003e\n\u003ch2 id=\"capítulo-4-três-backends-uma-forma-de-configuração\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-4-tr%c3%aas-backends-uma-forma-de-configura%c3%a7%c3%a3o\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 4: Três Backends, Uma Forma de Configuração'\"\u003e\u003c/a\u003e4 Capítulo 4: Três Backends, Uma Forma de Configuração\u003c/h2\u003e\u003cp\u003eAqui é onde a abstração de workload paga seu aluguel. A mesma estrofe de JSON deve rodar de três formas diferentes:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-7\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   fastfn.json workload\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            ├────────┬────────────┬──────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            │        │            │                  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            ▼        ▼            ▼                  ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       docker   native process  Firecracker     (future\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       native   (no container)  microVM on       backends)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       (fallback)                Linux/KVM\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eQual delas roda é em parte uma decisão de plataforma e em parte uma decisão de branch. Nesta branch (\u003ccode\u003efirecracker-simple-images\u003c/code\u003e), o Firecracker é o alvo em hosts Linux/KVM, e \u003ccode\u003edocker_native.go\u003c/code\u003e é o gerenciador de fallback — note a build tag no topo do arquivo (\u003ccode\u003ecli/internal/workloads/docker_native.go:1\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-8\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//go:build !linux\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003epackage\u003c/span\u003e \u003cspan class=\"nx\"\u003eworkloads\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEssa única linha faz mais trabalho do que parece. Ela diz \u0026ldquo;em não-Linux, use a implementação do gerenciador de workloads apoiada em Docker.\u0026rdquo; Em Linux o gerenciador Firecracker assume (\u003ccode\u003efirecracker_manager_linux.go\u003c/code\u003e). Ambas as implementações satisfazem a mesma interface interna \u003ccode\u003enativeImageWorkloadManager\u003c/code\u003e que \u003ccode\u003eprocess/runner.go\u003c/code\u003e conecta:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-9\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/process/runner.go (from the 6a54c11 diff)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003enativeImageWorkloadManager\u003c/span\u003e \u003cspan class=\"kd\"\u003einterface\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eStart\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eContext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eStop\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eContext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eStatePath\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eTrês métodos. Start sobe todos os workloads. Stop os derruba. \u003ccode\u003eStatePath()\u003c/code\u003e diz ao resto do sistema onde vive o arquivo de estado JSON, para que o lado Lua possa encontrá-lo. É isso. O gerenciador Docker constrói imagens, cria uma rede, inicia contêineres e abre portas publicadas. O gerenciador Firecracker constrói imagens, as converte em bundles, inicializa microVMs e as anexa a uma rede de pares por vsock. Mecânicas diferentes, contrato público idêntico.\u003c/p\u003e\n\u003cp\u003eO gerenciador docker-native é, honestamente, o docker-compose que eu me recusei a escrever. Ele cria uma rede por projeto (\u003ccode\u003ecli/internal/workloads/docker_native.go:90-98\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-10\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/docker_native.go:90-98\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003enetworkName\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;fastfn-\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nf\"\u003eshortHash\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eProjectDir\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eStatePath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ecli\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eNetworkCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003enetworkName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003edockertypes\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eNetworkCreate\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eCheckDuplicate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eDriver\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e         \u003cspan class=\"s\"\u003e\u0026#34;bridge\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eErrorf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;create docker network: %w\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003enetworkName\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003enetworkName\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEntão, para cada service e cada app, ele anexa o contêiner com dois aliases — o nome puro e um alias \u003ccode\u003e\u0026lt;name\u0026gt;.internal\u003c/code\u003e (\u003ccode\u003edocker_native.go:417-423\u003c/code\u003e) — de modo que apps dentro da rede podem alcançar \u003ccode\u003emysql.internal:3306\u003c/code\u003e enquanto o host alcança \u003ccode\u003e127.0.0.1:\u0026lt;published\u0026gt;\u003c/code\u003e. Essa separação é intencional: apps públicos alcançam o mundo externo através do gateway numa URL estável, services privados alcançam uns aos outros através do alias interno, e o host alcança portas publicadas para depuração.\u003c/p\u003e\n\u003cp\u003eUm service subindo se parece mais ou menos com isto:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-11\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/docker_native.go:251-266 (condensed)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003eservice\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003eServiceState\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e         \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eImage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e        \u003cspan class=\"nf\"\u003efirstNonEmpty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eImage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eDockerfile\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eImageDigest\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e  \u003cspan class=\"nx\"\u003edigest\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eHost\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e         \u003cspan class=\"s\"\u003e\u0026#34;127.0.0.1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ePort\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e         \u003cspan class=\"nx\"\u003ehostPort\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eInternalHost\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eName\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;.internal\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eInternalPort\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePort\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eContainerID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e  \u003cspan class=\"nx\"\u003econtainerID\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eHealth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e       \u003cspan class=\"nx\"\u003eWorkloadHealth\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003eUp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eReason\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;ok\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eVolume\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e       \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eVolume\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eBaseEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e      \u003cspan class=\"nf\"\u003ecloneEnvMap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eURL\u003c/span\u003e         \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eBuildServiceURL\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eHost\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e         \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePort\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e         \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalURL\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eBuildServiceURL\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalHost\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalPort\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eFunctionEnv\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eBuildFunctionServiceEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDuas observações. Primeiro, \u003ccode\u003eBuildServiceURL\u003c/code\u003e inspeciona o env e descobre o esquema — se vê \u003ccode\u003eMYSQL_USER\u003c/code\u003e constrói uma URL \u003ccode\u003emysql://\u003c/code\u003e, se vê \u003ccode\u003ePOSTGRES_USER\u003c/code\u003e uma \u003ccode\u003epostgres://\u003c/code\u003e, se vê \u003ccode\u003eREDIS_*\u003c/code\u003e uma \u003ccode\u003eredis://\u003c/code\u003e, caso contrário cai para \u003ccode\u003etcp://\u003c/code\u003e (\u003ccode\u003estate.go:143-157\u003c/code\u003e). É inferência alegre de URL, e significa que eu não preciso digitar \u003ccode\u003eDATABASE_URL\u003c/code\u003e à mão em noventa e nove por cento dos casos. Segundo, \u003ccode\u003eFunctionEnv\u003c/code\u003e é o saco de variáveis que toda função no projeto verá em tempo de requisição. Essa é a ponte: o service é privado, mas funções recebem \u003ccode\u003eSERVICE_MYSQL_HOST\u003c/code\u003e, \u003ccode\u003eSERVICE_MYSQL_PORT\u003c/code\u003e, \u003ccode\u003eSERVICE_MYSQL_URL\u003c/code\u003e, mais um alias direto \u003ccode\u003eMYSQL_HOST\u003c/code\u003e / \u003ccode\u003eMYSQL_PORT\u003c/code\u003e / \u003ccode\u003eMYSQL_URL\u003c/code\u003e quando o nome é inequívoco (\u003ccode\u003estate.go:107-123\u003c/code\u003e, \u003ccode\u003estate.go:193-203\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003eO que é, se você apertar os olhos, exatamente o que o docker-compose faz com sua injeção de \u003ccode\u003elinks\u003c/code\u003e e \u003ccode\u003edepends_on\u003c/code\u003e. Só que com um schema mais apertado e uma convenção de nomes mais afiada.\u003c/p\u003e\n\u003ch2 id=\"capítulo-5-como-o-lua-encontra-um-workload\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-5-como-o-lua-encontra-um-workload\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 5: Como o Lua Encontra um Workload'\"\u003e\u003c/a\u003e5 Capítulo 5: Como o Lua Encontra um Workload\u003c/h2\u003e\u003cp\u003eAgora a parte divertida. O gateway ainda é OpenResty + Lua — a mesma coisa da Parte 1. Como um processo Go de longa duração do lado da CLI diz ao Lua do lado do gateway que workloads existem e estão no ar?\u003c/p\u003e\n\u003cp\u003eAtravés de um arquivo JSON no disco. É isso.\u003c/p\u003e\n\u003cp\u003eO gerenciador de workloads da CLI escreve num arquivo de estado num caminho conhecido. O caminho é exportado como uma variável de ambiente para o gateway (\u003ccode\u003eprocess/runner.go\u003c/code\u003e no diff 6a54c11):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-12\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/process/runner.go (from 6a54c11)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003epath\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003estrings\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eTrimSpace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eworkloadMgr\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eStatePath\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e \u003cspan class=\"nx\"\u003epath\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ebaseEnv\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eappend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ebaseEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;FN_IMAGE_WORKLOADS_STATE_PATH=\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNo lado Lua, \u003ccode\u003eimage_workloads.lua\u003c/code\u003e lê esse caminho em toda requisição:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003elua\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-13\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- openresty/lua/fastfn/core/image_workloads.lua:22-48\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003estate_path\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos.getenv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;FN_IMAGE_WORKLOADS_STATE_PATH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eload_state\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003estate_path\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eapps\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{},\u003c/span\u003e \u003cspan class=\"n\"\u003eservices\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eread_json_file\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparsed\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e~=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eapps\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{},\u003c/span\u003e \u003cspan class=\"n\"\u003eservices\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eparsed.apps\u003c/span\u003e     \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparsed.apps\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e     \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed.apps\u003c/span\u003e     \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eparsed.services\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eparsed.services\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed.services\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eLeitura de JSON por requisição. Em produção você faria cache disso por algum TTL pequeno; em dev é exatamente a coisa certa. O status de saúde, as rotas, os hostnames internos, o estado do ciclo de vida — tudo naquele arquivo.\u003c/p\u003e\n\u003cp\u003eO roteamento é o próximo passo. O gateway já faz uma dança de correspondência longa para funções (coberta na Parte 1). Para workloads, ele pede ao \u003ccode\u003eimage_workloads.lua\u003c/code\u003e candidatos cujas rotas casam por prefixo com o caminho da requisição, e então os pontua pelo comprimento da rota (rotas mais longas vencem, porque \u003ccode\u003e/admin/api/v1/users\u003c/code\u003e é mais específico que \u003ccode\u003e/admin/*\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003elua\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-14\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- openresty/lua/fastfn/core/image_workloads.lua:123-151\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nc\"\u003eM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003epublic_http_candidates\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest_path\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003estate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eload_state\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ecandidates\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ekind\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eipairs\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;apps\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;services\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eworkload_name\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eipairs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esorted_keys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e]))\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eworkload\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e][\u003c/span\u003e\u003cspan class=\"n\"\u003eworkload_name\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eendpoint\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eipairs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eworkload_public_endpoints\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eworkload\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eendpoint.protocol\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;http\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;http\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eroutes\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eendpoint.routes\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eendpoint.routes\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eroute\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eipairs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroutes\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eroute_matches\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroute\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequest_path\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              \u003cspan class=\"n\"\u003ecandidates\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e#\u003c/span\u003e\u003cspan class=\"n\"\u003ecandidates\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ekind\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ekind\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;apps\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;app\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;service\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eworkload_name\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eworkload\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eworkload\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eendpoint\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eendpoint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eroute\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eroute\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eroute_length\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e#\u003c/span\u003e\u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroute\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecandidates\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDois detalhes de que gosto. O loop externo itera \u003ccode\u003e{\u0026quot;apps\u0026quot;,\u0026quot;services\u0026quot;}\u003c/code\u003e em vez de concatená-los, o que preserva a prioridade de apps sobre services quando a mesma rota é de algum modo reivindicada duas vezes (o que não deveria, mas código defensivo é barato). E \u003ccode\u003eroute_length\u003c/code\u003e segue junto com cada candidato para que quem chama possa escolher a correspondência mais longa, o que combina com a forma como todo roteador sensato resolve prefixos sobrepostos.\u003c/p\u003e\n\u003cp\u003eO gateway consome essa lista em tempo de requisição e faz proxy para o endpoint vencedor (\u003ccode\u003eopenresty/lua/fastfn/http/gateway.lua:950-969\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003elua\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-15\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- openresty/lua/fastfn/http/gateway.lua:950-969\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003erequest_host\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erequest_authority\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequest_host_values\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ematched_workload\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ematched_endpoint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eworkload_access_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ematch_public_workload\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eimage_workloads.public_http_candidates\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest_uri\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erequest_host\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erequest_authority\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003erequest_client_ip\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ematched_workload\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eapp_resp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eapp_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eexecute_public_workload_proxy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ematched_workload\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ematched_endpoint\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eapp_resp\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003estatus\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e502\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eapp_err\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;unavailable\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003estatus\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e503\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ewrite_response\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estatus\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                   \u003cspan class=\"n\"\u003ejson_error\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;public workload proxy failed: \u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eapp_err\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ewrite_response\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eapp_resp.status\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"mi\"\u003e502\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eapp_resp.headers\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{},\u003c/span\u003e \u003cspan class=\"n\"\u003eapp_resp.body\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eE aqui está o proxy de verdade — HTTP entra, HTTP sai, com os cabeçalhos hop-by-hop removidos na volta para que nada confunda o cliente (\u003ccode\u003egateway.lua:487-537\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003elua\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-16\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- openresty/lua/fastfn/http/gateway.lua:510-536\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_client.request\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eurl\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003estring.format\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;http://%s:%d%s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ehost\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eport\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003engx.var\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erequest_uri\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003emethod\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.req\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_method\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eheaders\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esanitize_app_request_headers\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003engx.req\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_headers\u003c/span\u003e\u003cspan class=\"p\"\u003e()),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ebody\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003etimeout_ms\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e30000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003emax_body_bytes\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e1024\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003efiltered\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003edrop\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;connection\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;keep-alive\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;transfer-encoding\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;content-length\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;upgrade\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003epairs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresp.headers\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003edrop\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e()]\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efiltered\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eresp.headers\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efiltered\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eQuinze megabytes de corpo máximo. Timeout de trinta segundos. Cabeçalhos hop-by-hop descartados. Este é o proxy reverso HTTP mais chato que você já leu, o que é exatamente o que você quer que um gateway seja.\u003c/p\u003e\n\u003cp\u003eA parte esperta não está no proxy. A parte esperta é que \u003cem\u003ea mesma fase ngx que resolve funções agora também resolve workloads\u003c/em\u003e, com uma prioridade conhecida, a partir do mesmo arquivo de estado JSON, usando as mesmas regras de firewall por host/CIDR. A coisa toda consegue ser uma superfície HTTP unificada.\u003c/p\u003e\n\u003ch2 id=\"capítulo-6-ciclo-de-vida-ou-transformando-uma-tabela-numa-máquina-de-estados\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-6-ciclo-de-vida-ou-transformando-uma-tabela-numa-m%c3%a1quina-de-estados\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 6: Ciclo de Vida (ou, Transformando uma Tabela numa Máquina de Estados)'\"\u003e\u003c/a\u003e6 Capítulo 6: Ciclo de Vida (ou, Transformando uma Tabela numa Máquina de Estados)\u003c/h2\u003e\u003cp\u003eUma vez que workloads existem, eles têm que passar por uma vida. Iniciando. Saudável. Insalubre. Parado. Pausado. Retomado. O ciclo de vida mantém a história operacional honesta.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-17\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ┌────────────┐   start()   ┌─────────────┐   healthcheck ok   ┌─────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    │  declared  │ ──────────▶ │  starting   │ ─────────────────▶ │ healthy │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    └────────────┘             └─────────────┘                    └────┬────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     │                                 │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     │ healthcheck fails               │ monitor tick\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     ▼                                 │ reports failure\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                               ┌─────────────┐                         ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                               │  unhealthy  │ ◀──────────────── ┌─────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                               └──────┬──────┘                    │ flapping│\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                      │ stop()                   └─────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                      ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                ┌───────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                │  stopped  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                └───────────┘\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEm state.go, as peças vivem num pequeno aglomerado de tipos (\u003ccode\u003estate.go:23-91\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-18\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/state.go:23-38\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003eWorkloadHealth\u003c/span\u003e \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eUp\u003c/span\u003e     \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e   \u003cspan class=\"s\"\u003e`json:\u0026#34;up\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eReason\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;reason,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003ePublicEndpointState\u003c/span\u003e \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eName\u003c/span\u003e          \u003cspan class=\"kt\"\u003estring\u003c/span\u003e   \u003cspan class=\"s\"\u003e`json:\u0026#34;name\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eProtocol\u003c/span\u003e      \u003cspan class=\"kt\"\u003estring\u003c/span\u003e   \u003cspan class=\"s\"\u003e`json:\u0026#34;protocol,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eHost\u003c/span\u003e          \u003cspan class=\"kt\"\u003estring\u003c/span\u003e   \u003cspan class=\"s\"\u003e`json:\u0026#34;host,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ePort\u003c/span\u003e          \u003cspan class=\"kt\"\u003eint\u003c/span\u003e      \u003cspan class=\"s\"\u003e`json:\u0026#34;port,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eContainerPort\u003c/span\u003e \u003cspan class=\"kt\"\u003eint\u003c/span\u003e      \u003cspan class=\"s\"\u003e`json:\u0026#34;container_port,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eListenPort\u003c/span\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e      \u003cspan class=\"s\"\u003e`json:\u0026#34;listen_port,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eRoutes\u003c/span\u003e        \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;routes,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eAllowHosts\u003c/span\u003e    \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;allow_hosts,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eAllowCIDRs\u003c/span\u003e    \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"s\"\u003e`json:\u0026#34;allow_cidrs,omitempty\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO sinal de saúde é uma struct de dois campos — up, e uma razão quando não está. Todo workload mantém uma. O gerenciador docker-native roda uma goroutine de monitoramento que acorda a cada dois segundos, inspeciona cada contêiner e atualiza o arquivo de estado quando qualquer coisa muda (\u003ccode\u003edocker_native.go:174-211\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-19\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/docker_native.go:174-211 (condensed)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eNativeManager\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003emonitor\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edefer\u003c/span\u003e \u003cspan class=\"nb\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edoneCh\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eticker\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eNewTicker\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eSecond\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edefer\u003c/span\u003e \u003cspan class=\"nx\"\u003eticker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eStop\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eselect\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;-\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003estopCh\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;-\u003c/span\u003e\u003cspan class=\"nx\"\u003eticker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eC\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"nx\"\u003echanged\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"nx\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eitem\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"k\"\u003erange\u003c/span\u003e \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003econtainers\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"nx\"\u003ehealth\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003einspectHealth\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eitem\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"c1\"\u003e// ... update m.state.Apps or m.state.Services,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e                \u003cspan class=\"c1\"\u003e//     setting changed = true when different\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003echanged\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"nx\"\u003e_\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ewriteState\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO comportamento de escrever-na-mudança importa. O lado Lua lê aquele arquivo de estado em toda requisição. Se o lado Go spammasse o arquivo a cada dois segundos incondicionalmente, toda requisição veria um arquivo novo e cada pequena leitura de fs seria desperdiçada. Escrever só quando algo \u003cem\u003erealmente se moveu\u003c/em\u003e mantém o arquivo estável por longos trechos, que é o tipo de coisa que você quer de um mecanismo de IPC que é, sendo generoso, um arquivo JSON.\u003c/p\u003e\n\u003cp\u003eA inicialização é serial de propósito: services sobem primeiro, depois apps (\u003ccode\u003edocker_native.go:100-128\u003c/code\u003e). Apps inicializam com o env do service já populado no seu ambiente — que é o momento em que a simetria limpa do arquivo de configuração se afirma como uma ordem de dependência. Services existem para que apps possam usá-los; portanto services inicializam primeiro.\u003c/p\u003e\n\u003cp\u003eO ponto de entrada da CLI é a mesma chata de duas linhas tanto em \u003ccode\u003edev\u003c/code\u003e quanto em \u003ccode\u003erun\u003c/code\u003e. Para \u003ccode\u003edev\u003c/code\u003e, do diff 6a54c11:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-20\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/cmd/dev.go (from 6a54c11 diff)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003eimageWorkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ehasImageWorkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nf\"\u003econfiguredImageWorkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003edevFatalf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Invalid apps/services config: %v\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nf\"\u003erunNative\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003econfiguredProjectRoot\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"nx\"\u003eabsPath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eimageWorkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003edevFatalf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Native dev failed: %v\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003ehasImageWorkloads\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003edevFatal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;apps/services are only supported in native mode for this branch; rerun with --native\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eAquela última ramificação é importante. Apps e services só funcionam com \u003ccode\u003e--native\u003c/code\u003e nesta branch; o modo de dev Docker clássico ainda é só funções. Essa é uma limitação conhecida, documentada explicitamente na referência de configuração (\u003ccode\u003edocs/en/reference/fastfn-config.md:13\u003c/code\u003e). Misturar os dois ciclos de vida no antigo caminho de gateway apoiado em Docker era o tipo de enrolação que eu decidi adiar. O modo nativo é o único caminho honesto adiante para esta funcionalidade.\u003c/p\u003e\n\u003cp\u003eO helper \u003ccode\u003econfiguredImageWorkloads\u003c/code\u003e é a cola. Ele lê as chaves \u003ccode\u003eapps\u003c/code\u003e e \u003ccode\u003eservices\u003c/code\u003e do viper e as normaliza através de \u003ccode\u003eworkloads.NormalizeAppSpecs\u003c/code\u003e / \u003ccode\u003eNormalizeServiceSpecs\u003c/code\u003e (\u003ccode\u003ecli/cmd/root.go:171-186\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-21\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/cmd/root.go:171-186 (from 6a54c11 diff)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003econfiguredImageWorkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eworkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e \u003cspan class=\"nx\"\u003eworkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eConfig\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eapps\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eappsSet\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003eworkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eNormalizeAppSpecs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eviper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eGet\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;apps\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eservices\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservicesSet\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003eworkloads\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eNormalizeServiceSpecs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eviper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eGet\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;services\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eApps\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eapps\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eServices\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eservices\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ecfg\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eappsSet\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003eservicesSet\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEssa é toda a fiação. O viper lê \u003ccode\u003efastfn.json\u003c/code\u003e, o pacote de workload o normaliza, e o runner nativo levanta um gerenciador cujo \u003ccode\u003eStatePath()\u003c/code\u003e é carimbado no ambiente de cada daemon de runtime e de cada worker OpenResty.\u003c/p\u003e\n\u003ch2 id=\"capítulo-7-a-virada-firecracker\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-7-a-virada-firecracker\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 7: A Virada Firecracker'\"\u003e\u003c/a\u003e7 Capítulo 7: A Virada Firecracker\u003c/h2\u003e\u003cp\u003eA esta altura tudo o que descrevi funciona num laptop com Docker, e nada do que descrevi precisa de Firecracker. Mas a branch inteira se chama \u003ccode\u003efirecracker-simple-images\u003c/code\u003e, então deixe-me dizer onde o Firecracker entra, e o que de fato mudou.\u003c/p\u003e\n\u003cp\u003eNum host Linux/KVM, o gerenciador não é \u003ccode\u003edocker_native.go\u003c/code\u003e. É \u003ccode\u003efirecracker_manager_linux.go\u003c/code\u003e. Mesma interface. Mesmo arquivo de estado. Mecânicas muito diferentes por baixo: a imagem OCI é convertida num bundle Firecracker (um kernel \u003ccode\u003evmlinux\u003c/code\u003e mais um \u003ccode\u003erootfs.ext4\u003c/code\u003e), o bundle é colocado em cache sob \u003ccode\u003e.fastfn/firecracker/images/\u003c/code\u003e, e um microVM inicializa com uma configuração de kernel mínima. O contrato público é idêntico: o workload escuta na sua porta declarada, o gateway faz proxy para ele, o arquivo de estado reporta a saúde.\u003c/p\u003e\n\u003cp\u003eO que é diferente — e é aqui que o commit \u003ccode\u003e5568b6c\u003c/code\u003e (\u0026ldquo;Add vsock peer networking for Firecracker workloads\u0026rdquo;) importa — é como o gateway de fato \u003cem\u003ealcança\u003c/em\u003e um convidado Firecracker. Um contêiner Docker comum publica uma porta numa bridge e você disca \u003ccode\u003e127.0.0.1:\u0026lt;hostport\u0026gt;\u003c/code\u003e. Um microVM Firecracker não tem essa conveniência por padrão. Então o commit introduz uma rede de pares baseada em vsock: um helper do lado do convidado (\u003ccode\u003ecli/internal/firecrackerguest/main.go\u003c/code\u003e, 455 linhas naquele commit) que termina o vsock, e um \u003ccode\u003eprivate_network.go\u003c/code\u003e do lado do host que costura as peças. O gateway ainda disca um \u003ccode\u003eInternalHost:InternalPort\u003c/code\u003e; o encanamento sob aquele host/porta é vsock em vez de TCP numa bridge. A abstração se mantém.\u003c/p\u003e\n\u003cp\u003eO commit de acompanhamento \u003ccode\u003e6fd5fec\u003c/code\u003e (\u0026ldquo;Keep Firecracker image workloads hot by default\u0026rdquo;) é o que tornou isto útil na prática. Sem ele, um workload Firecracker recém-inicializado se sentiria ótimo na primeira requisição e menos ótimo se algum dia fosse pausado. Com os padrões keep-hot, o workload permanece residente e é pré-aquecido na inicialização do \u003ccode\u003efastfn dev\u003c/code\u003e / \u003ccode\u003efastfn run\u003c/code\u003e. Os docs chamam isto de \u0026ldquo;speed-first\u0026rdquo; (\u003ccode\u003edocs/en/reference/fastfn-config.md:302-307\u003c/code\u003e): \u003ccode\u003eidle_action\u003c/code\u003e tem como padrão \u003ccode\u003erun\u003c/code\u003e, \u003ccode\u003eprewarm\u003c/code\u003e tem como padrão \u003ccode\u003etrue\u003c/code\u003e, tanto para apps quanto para services.\u003c/p\u003e\n\u003cp\u003eOs números de verdade — e estes são copiados e colados da matriz de benchmark em \u003ccode\u003edocs/en/explanation/performance-benchmarks.md\u003c/code\u003e — são a única razão pela qual estou disposto a chamar qualquer coisa disto de pronto:\u003c/p\u003e\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eCase\u003c/th\u003e\n\u003cth\u003eSource\u003c/th\u003e\n\u003cth style=\"text-align:right\"\u003eBuild/Pull\u003c/th\u003e\n\u003cth style=\"text-align:right\"\u003eFirst OK\u003c/th\u003e\n\u003cth style=\"text-align:right\"\u003eHot p50\u003c/th\u003e\n\u003cth style=\"text-align:right\"\u003eHot p95\u003c/th\u003e\n\u003cth style=\"text-align:right\"\u003eHot p99\u003c/th\u003e\n\u003cth\u003eSame PID\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eFlask (\u003ccode\u003eflask-compose\u003c/code\u003e)\u003c/td\u003e\n\u003ctd\u003eDockerfile repo\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e1168ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e5017ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e1.94ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e3.05ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e4.10ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eRegistry app (\u003ccode\u003etraefik/whoami:v1.10.2\u003c/code\u003e)\u003c/td\u003e\n\u003ctd\u003eRegistry image\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e98ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e2508ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e1.26ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e2.09ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e2.28ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eFastAPI + Postgres (\u003ccode\u003efastapi-realworld\u003c/code\u003e)\u003c/td\u003e\n\u003ctd\u003eDockerfile repo + private service\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e1202ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e17036ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e5.29ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e7.02ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e7.94ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eTwo equal \u003ccode\u003epostgres:16\u003c/code\u003e services\u003c/td\u003e\n\u003ctd\u003eSame OCI, same native \u003ccode\u003e5432\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e1246ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e22090ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e10.92ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e28.85ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e32.58ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eRust + Postgres (\u003ccode\u003erust-postgres\u003c/code\u003e)\u003c/td\u003e\n\u003ctd\u003eDockerfile repo + private service\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e35139ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e47602ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e2.66ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e3.86ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd style=\"text-align:right\"\u003e\u003ccode\u003e10.27ms\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003etrue\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eCinco linhas de uma matriz de vinte casos (snapshot de 1º de abril de 2026; lista completa em \u003ccode\u003edocs/en/explanation/performance-benchmarks.md:46-54\u003c/code\u003e). A leitura que faço desta tabela é:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBuild a frio + pré-aquecimento são segundos, às vezes dezenas de segundos para um build em Rust. Isso não é de graça. Mas acontece uma vez.\u003c/li\u003e\n\u003cli\u003eDepois do pré-aquecimento, o caminho quente é de baixos milissegundos de um dígito para apps leves e ainda de um dígito a baixos dois dígitos para os apoiados em banco de dados.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003esame_firecracker_pid = true\u003c/code\u003e em cada linha, o que significa que o loop quente está realmente reutilizando o mesmo microVM residente. O gateway não está recriando silenciosamente o Firecracker entre requisições.\u003c/li\u003e\n\u003cli\u003eDois services \u003ccode\u003epostgres:16\u003c/code\u003e idênticos podem compartilhar a mesma 5432 nativa desde que seus \u003cem\u003enomes\u003c/em\u003e de workload sejam diferentes. A rede privada é por workload; a porta é por processo dentro do seu próprio convidado.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eA ressalva honesta — que o próprio doc assume, e que estou feliz em repetir (\u003ccode\u003edocs/en/explanation/performance-benchmarks.md:94-139\u003c/code\u003e) — é que a matriz de 20 casos não é \u0026ldquo;vinte apps upstream com zero edições.\u0026rdquo; Alguns casos são upstream-como-estão. Alguns têm uma camada de benchmark por cima. Todos compartilham o mesmo caminho de runtime do FastFN, mas o harness não é um benchmark sem-toque para todos eles. Prefiro dizer isso em voz alta a envernizar.\u003c/p\u003e\n\u003cp\u003eE sim, \u003ccode\u003efd8a6b5\u003c/code\u003e (\u0026ldquo;Add image workload firewall and benchmark matrix\u0026rdquo;) é o commit que trouxe tanto o controle de acesso \u003ccode\u003eallow_hosts\u003c/code\u003e / \u003ccode\u003eallow_cidrs\u003c/code\u003e em portas públicas quanto a ferramentaria que produziu estes números. Vou cobrir o firewall num post posterior — é uma estética inteira em si mesma — mas a versão curta é: um app público pode ser travado a uma lista de hosts permitidos e/ou uma lista de CIDRs permitidos, ambas mostradas na função de pontuação do gateway que citei no Capítulo 5.\u003c/p\u003e\n\u003ch2 id=\"capítulo-8-funções-encontram-services-a-história-da-injeção\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-8-fun%c3%a7%c3%b5es-encontram-services-a-hist%c3%b3ria-da-inje%c3%a7%c3%a3o\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 8: Funções Encontram Services (a História da Injeção)'\"\u003e\u003c/a\u003e8 Capítulo 8: Funções Encontram Services (a História da Injeção)\u003c/h2\u003e\u003cp\u003eDe volta a um detalhe que passei por cima. Como exatamente uma função vê um service?\u003c/p\u003e\n\u003cp\u003eAtravés do env. Quando um service inicializa, o gerenciador chama \u003ccode\u003eBuildFunctionServiceEnv\u003c/code\u003e e guarda o resultado no estado do service (\u003ccode\u003estate.go:107-123\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-22\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cli/internal/workloads/state.go:107-123\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eBuildFunctionServiceEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceName\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e \u003cspan class=\"nx\"\u003eServiceState\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebaseEnv\u003c/span\u003e \u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eappendScopedServiceEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eserviceName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebaseEnv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eupper\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nf\"\u003eserviceEnvToken\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceName\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_HOST\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e         \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eHost\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_PORT\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e         \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eSprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePort\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_URL\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e          \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eURL\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_INTERNAL_HOST\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalHost\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_INTERNAL_PORT\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003efmt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eSprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalPort\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003estrings\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eTrimSpace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalURL\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;SERVICE_\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003eupper\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;_INTERNAL_URL\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eInternalURL\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eappendDirectServiceAlias\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eserviceName\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eHost\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePort\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eservice\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eURL\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003eout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNo lado Lua, quando uma função está prestes a ser invocada, o gateway puxa a união dos FunctionEnv de todos os services para o envelope de evento que passa ao daemon de runtime (\u003ccode\u003eopenresty/lua/fastfn/http/gateway.lua:1169-1174\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003elua\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-23\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- openresty/lua/fastfn/http/gateway.lua:1171-1174\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eservice_env\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eimage_workloads.function_env\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eservice_env\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e~=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eevent.env\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eservice_env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO que significa que uma função Python pode simplesmente fazer:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-24\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# a handler somewhere in functions/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ehost\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SERVICE_MYSQL_HOST\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eport\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SERVICE_MYSQL_PORT\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e  \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;SERVICE_MYSQL_URL\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003e…e funciona, quer o MySQL por trás seja um contêiner Docker no macOS ou um microVM Firecracker no Linux. Mesmos nomes de variáveis, mesma inferência de esquema de URL, mesmo código. A mudança de backend é invisível.\u003c/p\u003e\n\u003cp\u003eHá um segundo caminho também. Um \u003cstrong\u003eapp\u003c/strong\u003e precisa do env do service na inicialização do processo, não em tempo de requisição — porque um app Next.js lê \u003ccode\u003eprocess.env\u003c/code\u003e no \u003ccode\u003enext start\u003c/code\u003e, não por requisição. Então o gerenciador docker-native constrói um mapa appServiceEnv a partir de todos os services e o passa como o env de contêiner de todo app (\u003ccode\u003edocker_native.go:110-122\u003c/code\u003e, \u003ccode\u003e282-294\u003c/code\u003e). O app portanto vê as mesmas variáveis \u003ccode\u003eSERVICE_*\u003c/code\u003e, mas escopadas através de \u003ccode\u003eBuildAppServiceEnv\u003c/code\u003e em vez de \u003ccode\u003eBuildFunctionServiceEnv\u003c/code\u003e — a diferença é se você usa o hostname interno (para apps, que vivem na mesma rede privada) ou o host público (para funções, que vivem no host).\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-25\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        service discovery fan-out\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        ────────────────────────\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       ┌──────────────┐      BuildFunctionServiceEnv\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       │   service    │ ───────────────────────────▶  event.env\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       │  (e.g. mysql)│                               │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e       └──────┬───────┘                               ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              │                                ┌─────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              │                                │  function   │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              │      BuildAppServiceEnv        └─────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e              └──────────────────────▶\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                        container.Env\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                        │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                        ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                  ┌──────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                  │   app    │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                  └──────────┘\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDois consumidores. Uma fonte da verdade. Nenhum \u003ccode\u003eDATABASE_URL\u003c/code\u003e codificado à mão à vista.\u003c/p\u003e\n\u003ch2 id=\"capítulo-9-as-lições-que-de-fato-estou-levando\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-9-as-li%c3%a7%c3%b5es-que-de-fato-estou-levando\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 9: As Lições Que de Fato Estou Levando'\"\u003e\u003c/a\u003e9 Capítulo 9: As Lições Que de Fato Estou Levando\u003c/h2\u003e\u003cp\u003eOlhando para trás, para o diff de 2254 linhas, aqui está o que eu diria ao meu eu passado antes de começar.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUnificar o gateway é o ponto inteiro.\u003c/strong\u003e A tentação de dividir o HTTP em \u0026ldquo;o fastfn cuida das URLs de função\u0026rdquo; e \u0026ldquo;o docker-compose cuida de todo o resto\u0026rdquo; é enorme porque é o caminho de menor resistência. Mas toda divisão da superfície HTTP é um bug futuro em CORS, autenticação, observabilidade ou OpenAPI. Mantenha o gateway único. Dê a ele mais tipos de alvos, não mais amigos.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDois modelos de ciclo de vida podem coexistir se um arquivo de configuração faz a compilação.\u003c/strong\u003e Funções têm escopo de requisição. Workloads são de longa duração. Estes são genuinamente formas diferentes com modos de falha diferentes. Esconder ambos por trás de um \u003ccode\u003efastfn.json\u003c/code\u003e funcionou porque a camada de configuração compila ambas as formas para a mesma representação de runtime de \u0026ldquo;coisa-para-a-qual-o-gateway-faz-proxy\u0026rdquo;. Dois modelos de ciclo de vida, um arquivo de configuração, zero arrependimentos.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDeclare a origem da imagem exatamente uma vez.\u003c/strong\u003e A regra do exatamente-um em \u003ccode\u003eimage\u003c/code\u003e/\u003ccode\u003eimage_file\u003c/code\u003e/\u003ccode\u003edockerfile\u003c/code\u003e parece pedante no dia um e te salva de mensagens de erro ilegíveis no dia duzentos. Você quer que haja uma única forma de um engenheiro olhando para \u003ccode\u003efastfn.json\u003c/code\u003e saber de onde este workload está vindo.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUm arquivo JSON é uma superfície de IPC perfeitamente boa entre sua CLI e seu gateway, se você for cuidadoso.\u003c/strong\u003e Escrever só na mudança, ler por requisição, e exportar o caminho através de uma variável de ambiente é — contra minhas premissas — uma forma extremamente calma de mover informação entre um processo Go de longa duração e um processo OpenResty de longa duração. Existe um futuro em que isto vira um socket unix e um modelo de assinatura. Mas por ora, o arquivo é honesto e fácil de depurar com \u003ccode\u003ecat\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNomeie services pelo que eles são.\u003c/strong\u003e Eu quase não auto-gerei os aliases \u003ccode\u003eMYSQL_HOST\u003c/code\u003e / \u003ccode\u003eMYSQL_URL\u003c/code\u003e ao lado dos \u003ccode\u003eSERVICE_\u0026lt;NAME\u0026gt;_HOST\u003c/code\u003e. Então escrevi meu primeiro handler de verdade e lembrei que humanos não querem digitar \u003ccode\u003eSERVICE_MYSQL_HOST\u003c/code\u003e. Aliases diretos são uma funcionalidade de usabilidade fingindo ser uma convenção de nomes.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBenchmarks te mantêm honesto.\u003c/strong\u003e A tabela da matriz acima é a única razão pela qual eu acredito nas palavras \u0026ldquo;residente\u0026rdquo; e \u0026ldquo;quente\u0026rdquo; nos meus próprios docs. \u003ccode\u003esame_firecracker_pid = true\u003c/code\u003e significa que ninguém está secretamente reiniciando a VM entre minha requisição e meu p95. Esse check existe porque no início, \u003cem\u003enão\u003c/em\u003e era verdade, e o benchmark era a única coisa que me dizia. Meça a propriedade, não a intenção.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eE uma palavra sobre ressalvas.\u003c/strong\u003e Há muita coisa que deliberadamente não fiz nesta branch. Apps e services só funcionam com \u003ccode\u003e--native\u003c/code\u003e. Workloads Firecracker só funcionam em Linux/KVM. Usuários de macOS e Windows ganham o gerenciador Docker em vez de Firecracker por ora, e esse caminho não roda image workloads de jeito nenhum fora do modo nativo nesta branch. Rolling updates, blue/green, autoscaling — todos ausentes, e tudo bem estarem ausentes, porque o ponto desta fase era acertar a abstração, não as operações. Um gerenciador de workloads que faz três backends de forma limpa vale muito mais que um gerenciador de workloads que faz um backend com toda funcionalidade que o Kubernetes já lançou.\u003c/p\u003e\n\u003cp\u003eO que vem a seguir? Há duas direções que consigo ver com clareza. Uma é trazer o caminho de workload para o \u003ccode\u003efastfn dev\u003c/code\u003e em modo Docker para que usuários de macOS ganhem a mesma ergonomia sem Firecracker. A outra é tornar o firewall mais rico — mais que \u003ccode\u003eallow_hosts\u003c/code\u003e e \u003ccode\u003eallow_cidrs\u003c/code\u003e, em algo que possa expressar \u0026ldquo;este service só é alcançável a partir destes workloads específicos nesta rede privada.\u0026rdquo; Ambas parecem capítulos genuinamente novos, não notas de rodapé.\u003c/p\u003e\n\u003cp\u003ePor ora, a manchete é pequena e chata e correta: se você precisa de um Postgres ao lado das suas funções, você edita seu \u003ccode\u003efastfn.json\u003c/code\u003e, adiciona uma estrofe \u003ccode\u003eservices.postgres\u003c/code\u003e, e suas funções ganham \u003ccode\u003eSERVICE_POSTGRES_URL\u003c/code\u003e no seu ambiente. É isso.\u003c/p\u003e\n\u003cp\u003eFunções são uma ótima forma para requisição/resposta. Workloads são a forma para tudo que não é. E agora eles vivem no mesmo arquivo de configuração, no mesmo gateway, no mesmo endpoint de saúde, na mesma matriz de benchmark, com os mesmos padrões keep-hot.\u003c/p\u003e\n\u003cp\u003eEu não queria escrever docker-compose. Acabei escrevendo algo que rima com o décimo do docker-compose que eu de fato uso, e nada mais. O que, sendo honesto, é provavelmente a primeira vez na minha carreira em que construí \u003cem\u003emenos\u003c/em\u003e plataforma do que pensei que construiria.\u003c/p\u003e\n\u003cp\u003eVejo você na Parte 3, onde vou desmontar em detalhe a história do firewall, o encanamento de vsock e os padrões keep-hot. Até lá: um arquivo, uma rota, um workload, um gateway.\u003c/p\u003e\n",
        "language": "pt"
    }
    {
        "title" : "fastfn Parte 1: Eu Tinha um Problema (e Introduzi Lua na Minha Vida)",
        "date_published" : "2026-06-02T00:00:00Z",
        "date_modified" : "2026-06-02T00:00:00Z",
        "id" : "https://misael.org/pt/fastfn-lua-to-our-lives/",
        "url" : "https://misael.org/pt/fastfn-lua-to-our-lives/",
        "summary": "Como construir um FaaS pessoal acabou rimando com o FastCGI — runtimes poliglotas, um gateway em Lua e um protocolo de fio em JSON. A Parte 1 cobre o lado das funções; a Parte 2 cobre os serviços.",
        "content_html" : "\u003ch2 id=\"capítulo-1-a-dor-ou-por-que-um-hello-world-tem-sete-arquivos\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-1-a-dor-ou-por-que-um-hello-world-tem-sete-arquivos\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 1: A Dor, ou \u0026amp;ldquo;Por Que um Hello World Tem Sete Arquivos?\u0026amp;rdquo;'\"\u003e\u003c/a\u003e1 Capítulo 1: A Dor, ou \u0026ldquo;Por Que um Hello World Tem Sete Arquivos?\u0026rdquo;\u003c/h2\u003e\u003cp\u003eA coisa toda começou com uma reclamação que é quase constrangedor dizer em voz alta em 2026: eu queria largar um arquivo Python no disco e que ele fosse um endpoint HTTP. Só isso. Sem Dockerfile. Sem \u003ccode\u003erequirements.txt\u003c/code\u003e a menos que eu quisesse um. Sem boilerplate de \u003ccode\u003eapp = FastAPI()\u003c/code\u003e, sem invocação de \u003ccode\u003euvicorn\u003c/code\u003e, sem assistente de \u0026ldquo;criar um projeto\u0026rdquo; que deixa para trás dezessete arquivos de configuração que vou passar as próximas três tardes deletando. E — esta é a parte em que fiquei mais exigente — eu queria um cold start razoável. Não Lambda-cold, em que a primeira requisição da manhã parece um 404 com passos extras. Mornozinho. Em escala humana.\u003c/p\u003e\n\u003cp\u003eEu estava cansado da forma dos frameworks web modernos. Eles são lindos, escalam, têm ecossistemas, e também pedem que você compile mentalmente um modelo mental inteiro do mundo deles antes que sua primeira rota responda com \u003ccode\u003e{\u0026quot;hello\u0026quot;: \u0026quot;world\u0026quot;}\u003c/code\u003e. Para uma ferramenta interna descartável, isso é um imposto que você paga em atenção. Eu vinha pagando esse imposto havia uma década. Eu queria parar.\u003c/p\u003e\n\u003cp\u003eA outra coisa que eu queria — e este é o recurso que silenciosamente conduz o resto da história — era \u003cstrong\u003epoliglota por padrão\u003c/strong\u003e. Não poliglota no sentido de \u0026ldquo;microsserviços em linguagens diferentes conversando por gRPC\u0026rdquo;. Poliglota no sentido de que a mesma árvore de URLs pode ter \u003ccode\u003eget.users.py\u003c/code\u003e, \u003ccode\u003epost.orders.js\u003c/code\u003e e \u003ccode\u003eget.health.go\u003c/code\u003e, lado a lado na mesma pasta, atrás do mesmo gateway. Roteamento baseado em arquivos do Next.js para handlers baseados em arquivos, agnóstico de runtime. Esse era o sonho.\u003c/p\u003e\n\u003cp\u003eEntão o alvo estava claro: uma coisa Function-as-a-Service, mas local-first, com uma CLI como interface principal, e uma árvore de arquivos como banco de dados. Eu o chamei de \u003ccode\u003efastfn\u003c/code\u003e. O README descreve a ambição em uma linha: \u0026ldquo;Start with one file, a friendly CLI, and a route tree that can grow into a real API or SPA without a rewrite later\u0026rdquo; (\u003ccode\u003eREADME.md:8\u003c/code\u003e). O resto deste post é a história de como essa única frase se transformou em um gateway em Lua, um daemon Python persistente e um protocolo de fio com um prefixo de comprimento de 4 bytes. É uma história sobre descobrir, lentamente e com algum constrangimento, que eu estava reinventando o FastCGI.\u003c/p\u003e\n\u003ch2 id=\"capítulo-2-uma-breve-ligeiramente-injusta-história-do-cgi\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-2-uma-breve-ligeiramente-injusta-hist%c3%b3ria-do-cgi\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 2: Uma Breve, Ligeiramente Injusta História do CGI'\"\u003e\u003c/a\u003e2 Capítulo 2: Uma Breve, Ligeiramente Injusta História do CGI\u003c/h2\u003e\u003cp\u003eAntes de chegar ao que \u003ccode\u003efastfn\u003c/code\u003e é, tenho que falar sobre com o que ele rima.\u003c/p\u003e\n\u003ch3 id=\"cgi-o-serverless-original\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cgi-o-serverless-original\" class=\"header-mark\" aria-label=\"Header mark for 'CGI: o serverless original'\"\u003e\u003c/a\u003e2.1 CGI: o serverless original\u003c/h3\u003e\u003cp\u003eNo início havia o CGI. O Common Gateway Interface era serverless antes de serverless ser uma marca. Você colocava um script em \u003ccode\u003e/cgi-bin/\u003c/code\u003e, o servidor web fazia \u003ccode\u003efork\u003c/code\u003e e \u003ccode\u003eexec\u003c/code\u003e dele a cada requisição, ele lia a requisição de variáveis de ambiente e do stdin, escrevia a resposta no stdout e saía. O SO limpava tudo. Toda requisição era um processo. Todo processo era um universo que existia por 40 milissegundos e então morria.\u003c/p\u003e\n\u003cp\u003eIsso é maravilhoso. Isso também é um crime de desempenho.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-1\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eCGI request model (what dies for you every time)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  client                 web server                 /cgi-bin/hello.pl\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   HTTP GET /hello     |                            .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    | --------------------\u0026gt; | fork()                     .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                       | exec(\u0026#34;perl hello.pl\u0026#34;)  --\u0026gt; [ cold Perl VM ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                       |                            [ parse script ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                       |                            [ run handler ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                       | \u0026lt;-- stdout --              [ die ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    | \u0026lt;-- HTTP 200 --       |                            .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                       |                            .\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eToda requisição paga pela criação do processo, aquecimento do interpretador, importação de bibliotecas e desmontagem. Numa máquina de 1996 isso doía. Numa máquina de 2026 ainda dói, só que de forma diferente: o Python cache-cold gasta uma fatia não trivial de tempo só importando sua própria biblioteca padrão antes que seu handler escreva um único byte. Eu não fiz o profiling disso de ponta a ponta especificamente para o \u003ccode\u003efastfn\u003c/code\u003e, mas a ordem de grandeza é grande o suficiente para que o pool persistente no estilo FastCGI exista precisamente para amortizá-la.\u003c/p\u003e\n\u003ch3 id=\"fastcgi-a-correção-e-a-forma-que-acabei-copiando\" class=\"headerLink\"\u003e\n    \u003ca href=\"#fastcgi-a-corre%c3%a7%c3%a3o-e-a-forma-que-acabei-copiando\" class=\"header-mark\" aria-label=\"Header mark for 'FastCGI: a correção, e a forma que acabei copiando'\"\u003e\u003c/a\u003e2.2 FastCGI: a correção, e a forma que acabei copiando\u003c/h3\u003e\u003cp\u003eO FastCGI foi inventado para corrigir exatamente isso. A ideia é quase óbvia em retrospecto: não mate o handler depois de cada requisição. Mantenha um pequeno pool de processos handler vivos, deixe o servidor web conversar com eles por um socket Unix, e enquadre as requisições para poder multiplexar de forma limpa. O servidor web é o front-end; o pool de handlers é o back-end; entre eles flui um fluxo de registros prefixados por comprimento.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-2\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eFastCGI\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e \u003cspan class=\"n\"\u003emodel\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ewhat\u003c/span\u003e \u003cspan class=\"n\"\u003edoesn\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;t die for you)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eclient\u003c/span\u003e         \u003cspan class=\"n\"\u003eweb\u003c/span\u003e \u003cspan class=\"n\"\u003eserver\u003c/span\u003e           \u003cspan class=\"n\"\u003eunix\u003c/span\u003e \u003cspan class=\"n\"\u003esocket\u003c/span\u003e          \u003cspan class=\"n\"\u003ehandler\u003c/span\u003e \u003cspan class=\"n\"\u003epool\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e  \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eGET\u003c/span\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e                    \u003cspan class=\"o\"\u003e.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"o\"\u003e-----------\u0026gt;\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003epack\u003c/span\u003e \u003cspan class=\"n\"\u003erecord\u003c/span\u003e           \u003cspan class=\"o\"\u003e|\u003c/span\u003e                    \u003cspan class=\"o\"\u003e.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"o\"\u003e--------\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003elen\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e------------\u0026gt;\u003c/span\u003e       \u003cspan class=\"p\"\u003e[\u003c/span\u003e \u003cspan class=\"n\"\u003ealready\u003c/span\u003e \u003cspan class=\"n\"\u003ewarm\u003c/span\u003e \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e                    \u003cspan class=\"p\"\u003e[\u003c/span\u003e \u003cspan class=\"n\"\u003erun\u003c/span\u003e \u003cspan class=\"n\"\u003ehandler\u003c/span\u003e \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e              \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;-------\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003elen\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e------------\u003c/span\u003e        \u003cspan class=\"p\"\u003e[\u003c/span\u003e \u003cspan class=\"n\"\u003esleep\u003c/span\u003e \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;---\u003c/span\u003e \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"o\"\u003e--\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e                       \u003cspan class=\"o\"\u003e|\u003c/span\u003e                    \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eOs handlers são de vida longa. O interpretador está quente. O estado global do seu handler sobrevive entre requisições (para o bem e para o mal). O transporte é um socket entediante com registros enquadrados. Você troca o isolamento processo-por-requisição por algo mais próximo de uma chamada-pela-rede. É uma troca muito boa e é essencialmente o que os servidores WSGI modernos do Python, o PHP-FPM e — sejamos honestos — o pool de warm-start do AWS Lambda estão todos fazendo internamente.\u003c/p\u003e\n\u003cp\u003eEu não me propus a construir um FastCGI. Me propus a construir uma coisa serverless em que você larga um arquivo e ele vira uma rota. Acontece que, uma vez que você quer warm starts e handlers poliglotas e uma árvore de rotas endereçável por sistema de arquivos, o espaço de design o afunila em direção a algo que se parece estranhamente com o FastCGI vestindo um sobretudo de JSON. Mais sobre o sobretudo depois.\u003c/p\u003e\n\u003ch2 id=\"capítulo-3-eu-tinha-um-problema-e-introduzi-lua-na-minha-vida\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-3-eu-tinha-um-problema-e-introduzi-lua-na-minha-vida\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 3: Eu Tinha um Problema… e Introduzi Lua na Minha Vida'\"\u003e\u003c/a\u003e3 Capítulo 3: Eu Tinha um Problema… e Introduzi Lua na Minha Vida\u003c/h2\u003e\u003cp\u003eAqui está a parte em que o título faz sentido.\u003c/p\u003e\n\u003cp\u003eO gateway — a peça que termina o HTTP, lê a requisição, descobre qual arquivo no disco deve tratá-la, e encaminha a chamada — é a peça mais importante e mais irritante de qualquer FaaS. Ele tem que ser rápido. Tem que fazer hot-reload quando você salva um arquivo. Tem que fazer roteamento, autenticação, cookies, CORS, OpenAPI, e tem que fazer tudo isso sem se tornar um processo Node de 30 MB com um cold start de 400 ms.\u003c/p\u003e\n\u003cp\u003eTentei escrevê-lo em Go. Estava ok. Também era muita código para o que é essencialmente \u0026ldquo;pegue uma requisição, procure um arquivo, abra um socket, escreva, leia, escreva a resposta\u0026rdquo;. Então, uma noite, lembrei que o OpenResty — nginx com Lua embutido — já faz as partes difíceis (parsing de HTTP, TLS, epoll, memória compartilhada) e simplesmente me deixa programar a camada de política em uma linguagem de script com startup sub-milissegundo. Você não inicializa o OpenResty por requisição. O OpenResty inicializa uma vez, no início do processo, e então seu Lua roda dentro dos hooks de fase da requisição. Pense nisso como o servidor web convidando seu código para viver dentro do seu event loop como hóspede.\u003c/p\u003e\n\u003cp\u003eEntão introduzi Lua na minha vida. Não foi tanto uma decisão quanto uma árvore interna de opções que continuava se ramificando: a cada terceira coisa que eu precisava, acabava sendo \u0026ldquo;ah, posso simplesmente fazer isso em Lua e funciona\u0026rdquo;. Descoberta de rotas? Lua percorrendo um diretório. Tabela de roteamento em memória compartilhada? \u003ccode\u003engx.shared.DICT\u003c/code\u003e. Hot reload ao salvar arquivo? Um pequeno timer em Lua que faz stat no diretório de funções e reconstrói uma tabela de rotas em um shared dict. Parsing de cookie de sessão? Doze linhas:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/http/gateway.lua:70-82\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-3\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eparse_cookies\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecookie_header\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ecookies\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecookie_header\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e~=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003ecookie_header\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecookies\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003epair\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ecookie_header\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003egmatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[^;]+\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epair\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003ematch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;^%s*([^=]+)%s*=%s*(.-)%s*$\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ek\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003ek\u003c/span\u003e \u003cspan class=\"o\"\u003e~=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003ecookies\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ecookies\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDoze linhas. Sem \u003ccode\u003enpm install cookie-parser\u003c/code\u003e. Sem \u003ccode\u003ego get github.com/gorilla/sessions\u003c/code\u003e. Sem dependência transitiva que foi abandonada em 2019 e agora é mantida por um bot. Doze linhas de Lua que rodam dentro da fase de requisição do nginx, e o gateway agora entende cookies de sessão.\u003c/p\u003e\n\u003cdiv class=\"details admonition info open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"/\u003e\u003c/svg\u003e\u003c/span\u003eUma nota sobre Lua\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003eLua é uma linguagem estranha. É indexada a partir de 1. Tem uma única estrutura de dados (a tabela) e nenhum sistema de módulos de verdade até você apertar os olhos. Sua biblioteca padrão é quase agressivamente minimalista. Nada disso realmente atrapalha aqui — o gateway é curto, os hot paths ficam no LuaJIT, e as coisas que Lua \u003cem\u003enão\u003c/em\u003e tem (um runtime enorme, um ecossistema de pacotes, um sistema de tipos complicado) são exatamente as coisas que você não quer num hot path de requisição dentro do nginx.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003eE é perfeita para este trabalho. O runtime é pequeno, o código é pequeno, a latência é pequena, e o gateway inteiro — roteamento, parsing de sessão, marshalling da requisição, un-marshalling da resposta, endpoint OpenAPI, servir o Swagger UI — vive em um punhado de arquivos Lua sob \u003ccode\u003eopenresty/lua/fastfn/\u003c/code\u003e. A subárvore \u003ccode\u003ehttp/\u003c/code\u003e tem exatamente os módulos que você esperaria de um gateway de verdade: \u003ccode\u003egateway.lua\u003c/code\u003e, \u003ccode\u003eassets.lua\u003c/code\u003e, \u003ccode\u003ecatalog.lua\u003c/code\u003e, \u003ccode\u003eopenapi_endpoint.lua\u003c/code\u003e, \u003ccode\u003eswagger_ui.lua\u003c/code\u003e, \u003ccode\u003ereload.lua\u003c/code\u003e. O \u003ccode\u003egateway.lua\u003c/code\u003e principal tem 1341 linhas; o \u003ccode\u003eclient.lua\u003c/code\u003e inteiro que implementa o protocolo de fio tem 110 linhas. Vou citar a maior parte dele em um minuto.\u003c/p\u003e\n\u003ch3 id=\"a-tabela-de-roteamento-é-um-shared-dict\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-tabela-de-roteamento-%c3%a9-um-shared-dict\" class=\"header-mark\" aria-label=\"Header mark for 'A tabela de roteamento é um shared dict'\"\u003e\u003c/a\u003e3.1 A tabela de roteamento é um shared dict\u003c/h3\u003e\u003cp\u003eO truque que torna o gateway em Lua rápido não é esperto. É que a tabela de rotas não vive em um banco de dados, não vive no Redis, não vive nem mesmo na memória por worker. Ela vive em \u003ccode\u003engx.shared.fn_cache\u003c/code\u003e, uma zona de memória compartilhada do nginx legível por todo processo worker. Quando \u003ccode\u003efastfn dev\u003c/code\u003e inicia, Lua percorre o diretório de funções, constrói um índice — \u0026ldquo;\u003ccode\u003eGET /hello\u003c/code\u003e → \u003ccode\u003e/functions/get.hello.py\u003c/code\u003e → runtime \u003ccode\u003epython\u003c/code\u003e\u0026rdquo; — e o enfia no shared dict. Em mudança de arquivo, um chunk de reload em Lua o reconstrói. As requisições fazem uma única consulta no shared dict na fase \u003ccode\u003eaccess_by_lua\u003c/code\u003e e então despacham. Não há \u0026ldquo;framework\u0026rdquo;. Há uma hash table e uma convenção.\u003c/p\u003e\n\u003ch2 id=\"capítulo-4-fastcgi-vestindo-um-sobretudo-de-json\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-4-fastcgi-vestindo-um-sobretudo-de-json\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 4: FastCGI Vestindo um Sobretudo de JSON'\"\u003e\u003c/a\u003e4 Capítulo 4: FastCGI Vestindo um Sobretudo de JSON\u003c/h2\u003e\u003cp\u003eAgora o protocolo. Esta é a parte em que reconstruí o FastCGI por acidente.\u003c/p\u003e\n\u003cp\u003eQuando o gateway em Lua decidiu que \u003ccode\u003eGET /hello\u003c/code\u003e deve ser servido pelo runtime Python, ele precisa levar a requisição até o daemon Python. O daemon Python é um processo de vida longa escutando em um socket Unix (padrão \u003ccode\u003e/tmp/fastfn/fn-python.sock\u003c/code\u003e, configurável via \u003ccode\u003eFN_PY_SOCKET\u003c/code\u003e, que você pode ver em \u003ccode\u003esrv/fn/runtimes/python-daemon.py:25\u003c/code\u003e). O gateway abre o socket, escreve a requisição e lê a resposta.\u003c/p\u003e\n\u003cp\u003eO formato do frame é deliberadamente entediante:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-4\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"mi\"\u003e4\u003c/span\u003e \u003cspan class=\"n\"\u003ebytes\u003c/span\u003e            \u003cspan class=\"n\"\u003eN\u003c/span\u003e \u003cspan class=\"n\"\u003ebytes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e---------------\u003c/span\u003e    \u003cspan class=\"o\"\u003e----------------------------------------\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003ebig\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eendian\u003c/span\u003e  \u003cspan class=\"o\"\u003e|\u003c/span\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e                                      \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003euint32\u003c/span\u003e \u003cspan class=\"n\"\u003elen\u003c/span\u003e  \u003cspan class=\"o\"\u003e|\u003c/span\u003e    \u003cspan class=\"o\"\u003e|\u003c/span\u003e  \u003cspan class=\"n\"\u003eJSON\u003c/span\u003e \u003cspan class=\"n\"\u003epayload\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e  \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"o\"\u003e---------------\u003c/span\u003e    \u003cspan class=\"o\"\u003e----------------------------------------\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eQuatro bytes de comprimento big-endian. Então essa quantidade de bytes de JSON. Esse é o protocolo inteiro. É o enquadramento de registros do FastCGI, simplificado para um único tipo de registro, com o corpo do registro sendo JSON em vez da codificação binária chave-valor do FastCGI. Se o FastCGI vestisse um sobretudo e tentasse se passar por uma API REST moderna, é assim que ele se vestiria.\u003c/p\u003e\n\u003cp\u003eO lado Lua do fio é pequeno o suficiente para ser citado por completo. Aqui está o cliente que envia uma requisição e faz o parse de uma resposta:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/client.lua:22-106\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-5\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nc\"\u003eM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ecall_unix\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esocket_uri\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ereq_obj\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout_ms\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003epayload\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecjson.encode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ereq_obj\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003epayload\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;invalid_request\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;failed to encode request\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003econnect_timeout\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emath.max\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003emath.floor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etimeout_ms\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mf\"\u003e0.2\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eio_timeout\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emath.max\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout_ms\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003esock\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.socket\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etcp\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003esettimeouts\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003econnect_timeout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eio_timeout\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eio_timeout\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eok\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003econnect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esocket_uri\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eok\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eis_timeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;timeout\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;connect timeout\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;connect_error\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003esent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esend_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epack_u32\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e#\u003c/span\u003e\u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003esent\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eis_timeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esend_err\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;timeout\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;send timeout\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;send_error\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esend_err\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eheader\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eheader_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003ereceive\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eheader\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;receive_error\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eheader_err\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ebody_len\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eunpack_u32\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eheader\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ebody_len\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003ebody_len\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e1024\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;invalid_response\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;invalid frame length\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebody_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003ereceive\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebody_len\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esock\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e-- decode, validate, return\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA função manual \u003ccode\u003epack_u32\u003c/code\u003e é um bom lembrete de que o Lua 5.1 (que o OpenResty usa) não vem com \u003ccode\u003estring.pack\u003c/code\u003e, então eu a implemento eu mesmo:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/client.lua:5-11\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-6\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003epack_u32\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003en\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eb1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emath.floor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003en\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"mi\"\u003e16777216\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eb2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emath.floor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003en\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"mi\"\u003e65536\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eb3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emath.floor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003en\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eb4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003en\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"mi\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003estring.char\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eb1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eb2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eb3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eb4\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO limite do frame é 10 MB (\u003ccode\u003ebody_len \u0026gt; 10 * 1024 * 1024\u003c/code\u003e). O lado Python impõe um limite simétrico via \u003ccode\u003eFN_MAX_FRAME_BYTES\u003c/code\u003e, que tem padrão de 2 MB (\u003ccode\u003esrv/fn/runtimes/python-daemon.py:26\u003c/code\u003e). Sim, os dois padrões não batem. Isso é de propósito: o gateway é um pouco mais permissivo que o daemon para que um daemon mal configurado falhe no daemon, e não em uma confusa leitura pela metade no gateway. É o tipo de assimetria que parece errada em um code review e parece certa quando você está depurando um incidente em produção às 2 da manhã.\u003c/p\u003e\n\u003ch3 id=\"o-ciclo-de-vida-da-requisição-com-milissegundos-aproximados\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-ciclo-de-vida-da-requisi%c3%a7%c3%a3o-com-milissegundos-aproximados\" class=\"header-mark\" aria-label=\"Header mark for 'O ciclo de vida da requisição, com milissegundos aproximados'\"\u003e\u003c/a\u003e4.1 O ciclo de vida da requisição, com milissegundos aproximados\u003c/h3\u003e\u003cp\u003eUma vez que você tem o protocolo de fio, o ciclo de vida inteiro da requisição é fácil de desenhar. Aqui está uma requisição quente, com tempos aproximados que medi em um laptop de meados de 2024 rodando Linux:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-7\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.00\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eclient\u003c/span\u003e \u003cspan class=\"n\"\u003esends\u003c/span\u003e \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eGET\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e\u003cspan class=\"n\"\u003ehello\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.10\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003enginx\u003c/span\u003e \u003cspan class=\"n\"\u003eaccept\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eparse\u003c/span\u003e \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eheaders\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.20\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e \u003cspan class=\"n\"\u003eaccess_by_lua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eroute\u003c/span\u003e \u003cspan class=\"n\"\u003elookup\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003engx\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshared\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efn_cache\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.30\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ecjson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eencode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ereq_obj\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eJSON\u003c/span\u003e \u003cspan class=\"n\"\u003epayload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.40\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003engx\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esocket\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etcp\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\u003cspan class=\"n\"\u003econnect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/tmp/fastfn/fn-python.sock\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.50\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003esend\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"n\"\u003eB\u003c/span\u003e \u003cspan class=\"n\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e][\u003c/span\u003e\u003cspan class=\"n\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.60\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003ePython\u003c/span\u003e \u003cspan class=\"n\"\u003edaemon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003erecv\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e \u003cspan class=\"n\"\u003ebytes\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edecode\u003c/span\u003e \u003cspan class=\"n\"\u003elength\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.70\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003ePython\u003c/span\u003e \u003cspan class=\"n\"\u003edaemon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003erecv\u003c/span\u003e \u003cspan class=\"n\"\u003eN\u003c/span\u003e \u003cspan class=\"n\"\u003ebytes\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eloads\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.80\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003ePython\u003c/span\u003e \u003cspan class=\"n\"\u003edaemon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003edispatch\u003c/span\u003e \u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003ehandler\u003c/span\u003e \u003cspan class=\"n\"\u003efrom\u003c/span\u003e \u003cspan class=\"n\"\u003e_HANDLER_CACHE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e1.10\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003ehandler\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003ereturns\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;status\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;body\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;...\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e1.20\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003ePython\u003c/span\u003e \u003cspan class=\"n\"\u003edaemon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edumps\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esend\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"n\"\u003eB\u003c/span\u003e \u003cspan class=\"n\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e][\u003c/span\u003e\u003cspan class=\"n\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e1.30\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003erecv\u003c/span\u003e \u003cspan class=\"n\"\u003eheader\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erecv\u003c/span\u003e \u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecjson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e1.40\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003eLua\u003c/span\u003e \u003cspan class=\"n\"\u003econtent_by_lua\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003engx\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esay\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003et\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e1.50\u003c/span\u003e \u003cspan class=\"n\"\u003ems\u003c/span\u003e   \u003cspan class=\"n\"\u003enginx\u003c/span\u003e \u003cspan class=\"n\"\u003eflushes\u003c/span\u003e \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e \u003cspan class=\"n\"\u003eto\u003c/span\u003e \u003cspan class=\"n\"\u003eclient\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsses números são aproximados — eu não publiquei um benchmark de ponta a ponta para o caminho puro de função por si só, então pegue o detalhamento de milissegundos por linha como estimativa, não como evangelho. A forma está certa mesmo que os números exatos variem. Cold starts são uma conversa muito diferente e vou chegar lá.\u003c/p\u003e\n\u003ch2 id=\"capítulo-5-runtimes-poliglotas-ou-lambda-sem-a-amazon\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-5-runtimes-poliglotas-ou-lambda-sem-a-amazon\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 5: Runtimes Poliglotas, ou \u0026amp;ldquo;Lambda Sem a Amazon\u0026amp;rdquo;'\"\u003e\u003c/a\u003e5 Capítulo 5: Runtimes Poliglotas, ou \u0026ldquo;Lambda Sem a Amazon\u0026rdquo;\u003c/h2\u003e\u003cp\u003eO protocolo de fio é agnóstico de linguagem de propósito. O daemon Python é uma implementação. Há um daemon Node. Há um caminho para handlers em Rust, Go, PHP e Lua também. Todo daemon de runtime faz a mesma promessa: abrir um socket Unix, falar o protocolo de 4-bytes-de-comprimento + JSON, e quando uma requisição chega, despachar para uma função com a assinatura que o AWS Lambda tornou famosa.\u003c/p\u003e\n\u003cp\u003eAqui está um handler de verdade, dos exemplos do tutorial poliglota:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eexamples/functions/polyglot-tutorial/step-2/index.py:1-18\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-8\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejson\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ehandler\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003equery\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eevent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;query\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003equery\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;friend\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;status\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;headers\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;body\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edumps\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;step\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;message\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Hello \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e from Python.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;runtime\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;python\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsse é o contrato inteiro. \u003ccode\u003edef handler(event): return {...}\u003c/code\u003e. Forma-Lambda sem a Amazon. Forma-Cloudflare-Workers sem a Cloudflare. É também, se você apertar os olhos, apenas CGI com as variáveis-de-ambiente-e-stdin substituídas por um envelope JSON e um socket quente. A forma tem sido estável desde os anos 1990 porque é a forma certa: uma requisição é um dict, uma resposta é um dict, e o handler é uma função pura de um para o outro.\u003c/p\u003e\n\u003cp\u003eO daemon suporta três adaptadores de invocação explicitamente (\u003ccode\u003esrv/fn/runtimes/python-daemon.py:51-53\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-9\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_INVOKE_ADAPTER_NATIVE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;native\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_INVOKE_ADAPTER_AWS_LAMBDA\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;aws-lambda\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_INVOKE_ADAPTER_CLOUDFLARE_WORKER\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cloudflare-worker\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003e\u0026ldquo;native\u0026rdquo; é minha forma padrão. As outras duas existem para que você possa levantar um Lambda ou Cloudflare Worker existente para dentro do fastfn praticamente sem mudanças. Essa interoperabilidade não foi de graça; me custou um cuidadoso tradutor de formato de evento. Mas significa que você pode pegar código que você já tem rodando no serverless de outra pessoa e rodá-lo localmente com \u003ccode\u003efastfn dev\u003c/code\u003e, o que é genuinamente útil quando o console AWS do seu empregador está passando por uma de suas semanas.\u003c/p\u003e\n\u003ch3 id=\"um-pool-de-workers-dentro-do-daemon\" class=\"headerLink\"\u003e\n    \u003ca href=\"#um-pool-de-workers-dentro-do-daemon\" class=\"header-mark\" aria-label=\"Header mark for 'Um pool de workers dentro do daemon'\"\u003e\u003c/a\u003e5.1 Um pool de workers dentro do daemon\u003c/h3\u003e\u003cp\u003eO daemon Python não apenas despacha na thread principal. Ele tem um \u003ccode\u003eThreadPoolExecutor\u003c/code\u003e na frente de um pool de workers handler, controlado por um orçamento de slots. Os botões são todos variáveis de ambiente:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003esrv/fn/runtimes/python-daemon.py:38-40\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-10\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eRUNTIME_POOL_ACQUIRE_TIMEOUT_MS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;FN_PY_POOL_ACQUIRE_TIMEOUT_MS\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;5000\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eRUNTIME_POOL_IDLE_TTL_MS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;FN_PY_POOL_IDLE_TTL_MS\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;300000\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eRUNTIME_POOL_REAPER_INTERVAL_MS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;FN_PY_POOL_REAPER_INTERVAL_MS\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;2000\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEntão: timeout de aquisição de 5 segundos, TTL ocioso de 5 minutos, o reaper roda a cada 2 segundos. Esses são números de pool FastCGI vestidos com roupas Python. A thread reaper percorre o pool, mata workers ociosos além de seu TTL, e enxuga o footprint. O timeout de aquisição é o sinal de \u0026ldquo;estou na capacidade máxima, recue\u0026rdquo;.\u003c/p\u003e\n\u003ch2 id=\"capítulo-6-lua-até-o-fundo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-6-lua-at%c3%a9-o-fundo\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 6: Lua Até o Fundo'\"\u003e\u003c/a\u003e6 Capítulo 6: Lua Até o Fundo\u003c/h2\u003e\u003cp\u003eO Capítulo 3 foi o momento em que introduzi Lua na minha vida. O que eu não apreciei então é quão rapidamente ela colonizaria todo canto do plano de controle. Eu me propunha a resolver um problema em Lua — \u0026ldquo;deixe-me fazer o parse de um cookie\u0026rdquo; — e ao fim da tarde a tabela de roteamento, o gerador de OpenAPI, o limitador de taxa e o dashboard de admin eram todos Lua também, rodando no mesmo worker do nginx, compartilhando as mesmas zonas \u003ccode\u003engx.shared.DICT\u003c/code\u003e, sem nenhum salto interprocesso entre eles.\u003c/p\u003e\n\u003cp\u003eEste é o capítulo em que listo os lugares onde Lua roda. Lua não é uma arma secreta. É uma linguagem pequena com um JIT rápido, embutida em um servidor web que já faz as partes difíceis, e por acaso se encaixa neste problema como chave numa fechadura.\u003c/p\u003e\n\u003ch3 id=\"roteamento-e-descoberta-a-árvore-de-arquivos-é-o-banco-de-dados\" class=\"headerLink\"\u003e\n    \u003ca href=\"#roteamento-e-descoberta-a-%c3%a1rvore-de-arquivos-%c3%a9-o-banco-de-dados\" class=\"header-mark\" aria-label=\"Header mark for 'Roteamento e descoberta: a árvore de arquivos \u0026lt;em\u0026gt;é\u0026lt;/em\u0026gt; o banco de dados'\"\u003e\u003c/a\u003e6.1 Roteamento e descoberta: a árvore de arquivos \u003cem\u003eé\u003c/em\u003e o banco de dados\u003c/h3\u003e\u003cp\u003eO primeiro lugar onde Lua venceu foi o roteamento. As rotas não vivem em um arquivo YAML ou um objeto de configuração; elas vivem no sistema de arquivos. \u003ccode\u003ecore/routes.lua\u003c/code\u003e percorre o diretório de funções no boot, lê metadados de cada arquivo de handler, e constrói um catálogo de \u0026ldquo;\u003ccode\u003eGET /hello\u003c/code\u003e → Python → \u003ccode\u003eget.hello.py\u003c/code\u003e\u0026rdquo;. Ele então cacheia o catálogo em \u003ccode\u003engx.shared.fn_cache\u003c/code\u003e para que todo worker tenha a mesma visão de graça. O ponto de entrada da descoberta é só isto:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/routes.lua:2228-2237\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-11\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nc\"\u003eM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ediscover_functions\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eforce\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eforce\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eraw\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eCACHE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;catalog:raw\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eraw\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecjson.decode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eraw\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eparsed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e-- ... walk functions_root, populate catalog.runtimes / mapped_routes ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003ecore/routes.lua\u003c/code\u003e é o grandão — cerca de 3.245 linhas — porque o roteamento é onde toda preocupação eventualmente se encontra: métodos, prefixos reservados, conflitos, ranks de fonte. Ao lado dele fica \u003ccode\u003ecore/fs.lua\u003c/code\u003e (cerca de 392 linhas), um pequeno wrapper FFI em torno de \u003ccode\u003estat\u003c/code\u003e, \u003ccode\u003eopendir\u003c/code\u003e, \u003ccode\u003ereaddir\u003c/code\u003e e amigos. Ele existe porque o FFI do LuaJIT me permite pular o módulo C LuaFileSystem e chamar a libc diretamente, mantendo a imagem do OpenResty enxuta:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/fs.lua:263-280\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-12\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nc\"\u003eM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elist_dirs_recursive\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eskip_fn\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eout\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eM.is_dir\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003ewalk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edir\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eskip_fn\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;function\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eskip_fn\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edir\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e#\u003c/span\u003e\u003cspan class=\"n\"\u003eout\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edir\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003e_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003echild\u003c/span\u003e \u003cspan class=\"kr\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eipairs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eM.list_dirs\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edir\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"kr\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"n\"\u003ewalk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echild\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ewalk\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003etable.sort\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eout\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsse é o walk do sistema de arquivos. \u003ccode\u003ereaddir\u003c/code\u003e no mundo Lua, com um \u003ccode\u003eskip_fn\u003c/code\u003e plugável para podar \u003ccode\u003e.git\u003c/code\u003e, \u003ccode\u003enode_modules\u003c/code\u003e, \u003ccode\u003e__pycache__\u003c/code\u003e e \u003ccode\u003e.fastfn\u003c/code\u003e. Ele retorna uma lista ordenada de diretórios na qual tanto \u003ccode\u003eroutes.lua\u003c/code\u003e quanto \u003ccode\u003ewatchdog.lua\u003c/code\u003e se apoiam. O que me fez pensar: se as rotas são uma árvore no disco, então a spec OpenAPI é uma projeção dessa mesma árvore, e o dashboard é apenas uma UI sobre ela.\u003c/p\u003e\n\u003ch3 id=\"openapi-de-graça\" class=\"headerLink\"\u003e\n    \u003ca href=\"#openapi-de-gra%c3%a7a\" class=\"header-mark\" aria-label=\"Header mark for 'OpenAPI, de graça'\"\u003e\u003c/a\u003e6.2 OpenAPI, de graça\u003c/h3\u003e\u003cp\u003eAquela ideia de \u0026ldquo;projeção da mesma árvore\u0026rdquo; se transformou em \u003ccode\u003ecore/openapi.lua\u003c/code\u003e, um módulo de 1.309 linhas que pega o catálogo produzido por \u003ccode\u003eroutes.lua\u003c/code\u003e e emite um documento OpenAPI 3. Não há decorators nas funções de handler. Não há \u003ccode\u003e@app.route(\u0026quot;/hello\u0026quot;)\u003c/code\u003e. O handler é apenas \u003ccode\u003edef handler(event): return {...}\u003c/code\u003e, e a spec é gerada a partir da árvore de rotas mais uma fina camada de metadados por função (\u003ccode\u003efn.config.json\u003c/code\u003e, se o handler se deu ao trabalho de escrever um).\u003c/p\u003e\n\u003cp\u003eO lado HTTP disso é um arquivo Lua de 101 linhas que liga tudo:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/http/openapi_endpoint.lua:92-101\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-13\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003espec\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eopenapi.build\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecatalog\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eserver_url\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eserver_url\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eruntime_order\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eroutes_mod.get_runtime_order\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003einclude_internal\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eenv_bool\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;FN_OPENAPI_INCLUDE_INTERNAL\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003einvoke_meta_lookup\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003einvoke_meta_lookup\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003engx.status\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003engx.header\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003engx.say\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecjson.encode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003espec\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsse é o endpoint \u003ccode\u003e/openapi.json\u003c/code\u003e inteiro. Recarregue seu handler, acesse a URL, a spec o reflete. O Swagger UI (\u003ccode\u003ehttp/swagger_ui.lua\u003c/code\u003e, 119 linhas) serve uma página estática que aponta para aquele JSON e você tem docs interativas sem rodar um servidor de docs separado. O gerador de OpenAPI também se apoia em \u003ccode\u003ecore/invoke_rules.lua\u003c/code\u003e para normalizar a política de invocação por função — quais métodos são permitidos, quais rotas são reservadas:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/invoke_rules.lua:4-14\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-14\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eM.ALLOWED_METHODS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eGET\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ePOST\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ePUT\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003ePATCH\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eDELETE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eM.RESERVED_ROUTE_PREFIXES\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;/_fn\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;/console\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO que me fez perceber quanto das ferramentas de API modernas é casamento de strings e allowlists. A \u0026ldquo;spec\u0026rdquo; não é uma spec, é uma visão.\u003c/p\u003e\n\u003ch3 id=\"observabilidade-e-limites-taxa-concorrência-saúde\" class=\"headerLink\"\u003e\n    \u003ca href=\"#observabilidade-e-limites-taxa-concorr%c3%aancia-sa%c3%bade\" class=\"header-mark\" aria-label=\"Header mark for 'Observabilidade e limites: taxa, concorrência, saúde'\"\u003e\u003c/a\u003e6.3 Observabilidade e limites: taxa, concorrência, saúde\u003c/h3\u003e\u003cp\u003eO truque de memória compartilhada que torna o roteamento barato é o mesmo truque que torna os limites de concorrência por função baratos. \u003ccode\u003ecore/limits.lua\u003c/code\u003e tem 133 linhas, a maioria delas boilerplate, e o mecanismo inteiro é uma dança de \u003ccode\u003eincr\u003c/code\u003e/\u003ccode\u003edecr\u003c/code\u003e em uma zona \u003ccode\u003engx.shared.fn_conc\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/limits.lua:22-39\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-15\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003efunction\u003c/span\u003e \u003cspan class=\"nc\"\u003eM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003etry_acquire\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edict\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efn_key\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003elimit\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003elimit\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003elimit\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ekey\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ekey_for\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efn_key\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edict\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eincr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;counter_error:\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ecurrent\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003elimit\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edict\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eincr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;busy\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEssa função é a primitiva inteira de \u0026ldquo;não mais que N invocações em andamento da função X em todo o gateway\u0026rdquo;. \u003ccode\u003edict:incr\u003c/code\u003e é atômico entre workers porque \u003ccode\u003engx.shared.DICT\u003c/code\u003e é memória compartilhada com um lock por baixo. Não há round trip ao Redis. Não há pod de admission-controller. É um contador na RAM, e ele está correto porque o nginx me dá ele correto.\u003c/p\u003e\n\u003cp\u003eSobre os limites fica \u003ccode\u003ecore/watchdog.lua\u003c/code\u003e (299 linhas), um watcher \u003ccode\u003einotify\u003c/code\u003e do Linux escrito em LuaJIT FFI. Ele abre \u003ccode\u003einotify_init1(IN_NONBLOCK | IN_CLOEXEC)\u003c/code\u003e, percorre a árvore de funções, adiciona um watch em todo subdiretório, e agenda um reload com debounce sempre que um arquivo muda:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/watchdog.lua:268-284\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-16\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eok_poll\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epoll_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.timer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eevery\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epoll_interval\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epremature\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003epremature\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003echanged\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eread_err\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edrain_events\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e-- ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003epending_since\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003engx.now\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"n\"\u003epending_since\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"n\"\u003edebounce_s\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003epending_since\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eschedule_reload\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eUm debounce de 150 ms, um callback de reload compartilhado, e a tabela de roteamento se reconstrói no lugar. Salve um arquivo, e a rota está no ar antes de eu voltar para o navegador.\u003c/p\u003e\n\u003ch3 id=\"agendamento-in-process-ngxtimerat-como-cron\" class=\"headerLink\"\u003e\n    \u003ca href=\"#agendamento-in-process-ngxtimerat-como-cron\" class=\"header-mark\" aria-label=\"Header mark for 'Agendamento in-process: \u0026lt;code\u0026gt;ngx.timer.at\u0026lt;/code\u0026gt; como cron'\"\u003e\u003c/a\u003e6.4 Agendamento in-process: \u003ccode\u003engx.timer.at\u003c/code\u003e como cron\u003c/h3\u003e\u003cp\u003eSe eu posso agendar um reload com \u003ccode\u003engx.timer\u003c/code\u003e, posso agendar qualquer coisa. \u003ccode\u003ecore/scheduler.lua\u003c/code\u003e (1.712 linhas) e \u003ccode\u003ecore/jobs.lua\u003c/code\u003e (993 linhas) juntos implementam um executor de jobs ao estilo cron, ciente de retentativas, opcionalmente persistido, que vive \u003cem\u003edentro\u003c/em\u003e do worker do nginx. Sem daemon \u003ccode\u003ecron\u003c/code\u003e separado, sem Celery, sem RabbitMQ:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/core/scheduler.lua:1659-1667\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-17\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eok\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.timer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eevery\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003einterval\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kr\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epremature\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003epremature\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eok2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerr2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epcall\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etick_once\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eok2\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003engx.log\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003engx.ERR\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;scheduler tick failed: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etostring\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerr2\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsse único \u003ccode\u003engx.timer.every\u003c/code\u003e é o loop de tick. A cada tick ele percorre os agendamentos ativos, verifica as expressões cron contra o relógio de parede, e enfileira execuções via \u003ccode\u003engx.timer.at(0, ...)\u003c/code\u003e. Retentativas com backoff exponencial saem da mesma primitiva. A persistência é um blob JSON escrito no disco a cada 15 segundos. É bastante código para uma ideia pequena: \u003cem\u003eo event loop que eu já tenho é agendador suficiente para um FaaS de nó único.\u003c/em\u003e\u003c/p\u003e\n\u003ch4 id=\"um-exemplo-concreto-um-digest-de-ia-do-telegram-que-roda-a-cada-hora\" class=\"headerLink\"\u003e\n    \u003ca href=\"#um-exemplo-concreto-um-digest-de-ia-do-telegram-que-roda-a-cada-hora\" class=\"header-mark\" aria-label=\"Header mark for 'Um exemplo concreto: um digest de IA do Telegram que roda a cada hora'\"\u003e\u003c/a\u003e6.4.1 Um exemplo concreto: um digest de IA do Telegram que roda a cada hora\u003c/h4\u003e\u003cp\u003eO scheduler é abstrato até você ver uma função que o usa. O mais limpo no repositório é \u003ca href=\"https://github.com/misaelzapata/fastfn/tree/main/examples/functions/node/telegram-ai-digest\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003eexamples/functions/node/telegram-ai-digest\u003c/code\u003e\u003c/a\u003e — uma função Node que puxa mensagens de um grupo do Telegram, as resume com a OpenAI, e envia o digest de volta a um chat. O que a torna interessante para esta seção é o bloco \u003ccode\u003eschedule\u003c/code\u003e em sua config:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eexamples/functions/node/telegram-ai-digest/fn.config.json\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-18\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;timeout_ms\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e30000\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;invoke\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;summary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Telegram AI digest: fetches group messages, summarizes with OpenAI, sends digest\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;methods\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;GET\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;content_type\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;schedule\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;enabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;every_seconds\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e3600\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;method\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;GET\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEssa é a interface inteira de \u0026ldquo;transforme isto em um cron job\u0026rdquo;: uma estrofe \u003ccode\u003eschedule\u003c/code\u003e de três linhas na própria config da função, ao lado do handler. Sem crontab externo, sem passo de registro separado, sem pipeline YAML. Quando o fastfn inicializa, o scheduler lê o \u003ccode\u003efn.config.json\u003c/code\u003e de cada função, vê que esta tem um \u003ccode\u003eschedule.enabled = true\u003c/code\u003e, e a adiciona ao loop de tick com \u003ccode\u003eevery_seconds = 3600\u003c/code\u003e. A cada hora, o scheduler emite uma requisição \u003ccode\u003eGET\u003c/code\u003e interna para a função como se um cliente a tivesse chamado — mesmo roteamento, mesmo handler, mesmo logging — e a função faz seu trabalho.\u003c/p\u003e\n\u003cp\u003eO mesmo padrão aparece nos exemplos irmãos: \u003ca href=\"https://github.com/misaelzapata/fastfn/tree/main/examples/functions/node/telegram-ai-reply\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003etelegram-ai-reply\u003c/code\u003e\u003c/a\u003e é um webhook (handler \u003ccode\u003ePOST\u003c/code\u003e, sem schedule — o Telegram o chama quando uma mensagem chega), \u003ca href=\"https://github.com/misaelzapata/fastfn/tree/main/examples/functions/node/telegram-send\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003etelegram-send\u003c/code\u003e\u003c/a\u003e é uma função no estilo biblioteca que você pode invocar de outros handlers para enviar uma mensagem (\u003ccode\u003edry_run\u003c/code\u003e por padrão, que é o tipo de detalhe de segurança que aprendi a adicionar depois do segundo bot que deveria ser silencioso). Três funções, três ciclos de vida — webhook, chamada de biblioteca, cron — e todos eles são apenas arquivos em \u003ccode\u003efunctions/\u003c/code\u003e. A única coisa que torna o digest \u0026ldquo;um cron\u0026rdquo; são aquelas três linhas de config.\u003c/p\u003e\n\u003cp\u003eGosto desta forma porque o \u0026ldquo;modelo de deploy\u0026rdquo; da função e seu \u0026ldquo;modelo de invocação\u0026rdquo; estão no mesmo lugar. Se um colega de equipe se pergunta \u0026ldquo;como isto roda?\u0026rdquo;, ele abre \u003ccode\u003efn.config.json\u003c/code\u003e, vê \u003ccode\u003eschedule.every_seconds = 3600\u003c/code\u003e, e sabe. Sem caçar por um arquivo cron em um servidor ao qual ele não tem acesso ssh.\u003c/p\u003e\n\u003ch4 id=\"config-e-tokens-fnenvjson-e-a-flag-is_secret\" class=\"headerLink\"\u003e\n    \u003ca href=\"#config-e-tokens-fnenvjson-e-a-flag-is_secret\" class=\"header-mark\" aria-label=\"Header mark for 'Config e tokens: \u0026lt;code\u0026gt;fn.env.json\u0026lt;/code\u0026gt; e a flag \u0026lt;code\u0026gt;is_secret\u0026lt;/code\u0026gt;'\"\u003e\u003c/a\u003e6.4.2 Config e tokens: \u003ccode\u003efn.env.json\u003c/code\u003e e a flag \u003ccode\u003eis_secret\u003c/code\u003e\u003c/h4\u003e\u003cp\u003eA função de digest agendada é inútil sem dois segredos e um identificador: um token de bot do Telegram, uma chave de API da OpenAI, e o ID do chat para o qual postar. Este é o ponto em todo tutorial de FaaS em que você ou enrola sobre variáveis de ambiente ou cola um parágrafo sobre o Vault. O fastfn não faz nenhum dos dois. Ele coloca a config ao lado da função, em um arquivo irmão chamado \u003ccode\u003efn.env.json\u003c/code\u003e, com uma pequena quantidade de estrutura que torna o \u003cem\u003esignificado\u003c/em\u003e de cada valor explícito:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eexamples/functions/node/telegram-ai-digest/fn.env.json\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-19\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;TELEGRAM_BOT_TOKEN\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;set-me\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;is_secret\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;TELEGRAM_CHAT_ID\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;set-me\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;is_secret\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;OPENAI_API_KEY\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;set-me\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;is_secret\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDuas coisas a notar. Primeiro, toda chave é um objeto, não uma string nua — o valor vive em \u003ccode\u003e.value\u003c/code\u003e, e os metadados vivem ao lado dele. Segundo, aquele booleano \u003ccode\u003eis_secret\u003c/code\u003e é carga estrutural. O runtime o usa para decidir se um valor é mascarado nos logs e no dashboard de admin, se ele pode ser ecoado de volta por um endpoint \u003ccode\u003e_fn/ui_state\u003c/code\u003e, e se o botão \u0026ldquo;view\u0026rdquo; do dashboard tem permissão para revelá-lo em texto claro. \u003ccode\u003eTELEGRAM_CHAT_ID\u003c/code\u003e não é um segredo — é apenas um número, e você vai querer vê-lo na UI enquanto está depurando \u0026ldquo;por que meu digest não apareceu\u0026rdquo;. \u003ccode\u003eTELEGRAM_BOT_TOKEN\u003c/code\u003e é — e se você acidentalmente o vazar para os logs, a reação do Telegram é invalidar o token, então o scheduler pararia de funcionar silenciosamente até você perceber. A flag \u003ccode\u003eis_secret\u003c/code\u003e é a diferença inteira entre esses dois resultados.\u003c/p\u003e\n\u003cp\u003eO valor \u003ccode\u003e\u0026quot;\u0026lt;set-me\u0026gt;\u0026quot;\u003c/code\u003e também não é um bug — é um padrão que o repositório usa deliberadamente. O handler em \u003ccode\u003ecore.js\u003c/code\u003e trata \u003ccode\u003e\u0026lt;set-me\u0026gt;\u003c/code\u003e, \u003ccode\u003eset-me\u003c/code\u003e, \u003ccode\u003echangeme\u003c/code\u003e, \u003ccode\u003e\u0026lt;changeme\u0026gt;\u003c/code\u003e e \u003ccode\u003ereplace-me\u003c/code\u003e como sentinelas \u0026ldquo;não definidos\u0026rdquo;:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eexamples/functions/node/telegram-ai-digest/core.js:1-8\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-20\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003eisUnsetConfigValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"kc\"\u003eundefined\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003es\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nx\"\u003etrim\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nx\"\u003es\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003es\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003etoLowerCase\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;set-me\u0026gt;\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;set-me\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;changeme\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;changeme\u0026gt;\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003el\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;replace-me\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eIsso significa que você pode commitar \u003ccode\u003efn.env.json\u003c/code\u003e no git com placeholders \u003ccode\u003e\u0026quot;\u0026lt;set-me\u0026gt;\u0026quot;\u003c/code\u003e, e a função falha de forma limpa em vez de fingir rodar com uma string vazia. Deploys reais substituem esses placeholders (no dashboard, através da API, ou editando o arquivo em um host controlado) por valores reais; entradas \u003ccode\u003eis_secret: true\u003c/code\u003e são mascaradas imediatamente ao salvar.\u003c/p\u003e\n\u003cp\u003eDentro do handler, o runtime entrega esses valores em \u003ccode\u003eevent.env\u003c/code\u003e, ao lado do \u003ccode\u003eevent.method\u003c/code\u003e, \u003ccode\u003eevent.query\u003c/code\u003e, etc. com escopo de requisição. O \u003ccode\u003ecore.js\u003c/code\u003e do digest os lê com um pequeno fallback que prefere o env local da função, recorrendo ao \u003ccode\u003eprocess.env\u003c/code\u003e ambiente que o daemon colocou na allowlist no startup:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eexamples/functions/node/telegram-ai-digest/core.js:86-88\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-21\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ebotToken\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003echooseConfigValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTELEGRAM_BOT_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTELEGRAM_BOT_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003echatId\u003c/span\u003e   \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003echooseConfigValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTELEGRAM_CHAT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e   \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTELEGRAM_CHAT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eapiKey\u003c/span\u003e   \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003echooseConfigValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eOPENAI_API_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e     \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eOPENAI_API_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsta é a forma de config à qual eu sempre volto: um arquivo JSON ao lado do handler, um valor por segredo, uma flag \u003ccode\u003eis_secret\u003c/code\u003e explícita que dirige o mascaramento em toda superfície downstream, e um \u003ccode\u003eevent.env\u003c/code\u003e ao nível da linguagem que torna a dependência do handler nesses valores trivialmente grepável. É, novamente, não uma ideia nova — é basicamente a mesma forma das config vars do Heroku ou de um par \u003ccode\u003eSecret\u003c/code\u003e/\u003ccode\u003eConfigMap\u003c/code\u003e do Kubernetes. Mas vive na mesma pasta que o código que o usa, é versionável com placeholders seguros, e não requer um gerenciador de segredos externo para começar.\u003c/p\u003e\n\u003cp\u003eColoque o scheduler, a config e o handler em um diretório, e o digest do Telegram passa de \u0026ldquo;um vago cron job em algum lugar\u0026rdquo; para \u0026ldquo;quatro arquivos que posso ler em um minuto e entregar a um colega de equipe\u0026rdquo;.\u003c/p\u003e\n\u003ch3 id=\"o-dashboard-é-lua\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-dashboard-%c3%a9-lua\" class=\"header-mark\" aria-label=\"Header mark for 'O dashboard é Lua'\"\u003e\u003c/a\u003e6.5 O dashboard é Lua\u003c/h3\u003e\u003cp\u003eO lugar onde Lua mais me surpreendeu foi o console de admin. Eu não me propus a escrever uma aplicação web em Lua. Eu assumia que eventualmente conectaria um pequeno SPA, provavelmente Vue ou Svelte, porque \u0026ldquo;todo mundo faz isso\u0026rdquo;. Então escrevi \u003ccode\u003econsole/login_endpoint.lua\u003c/code\u003e em uma tarde e percebi que não precisava do SPA.\u003c/p\u003e\n\u003cp\u003eO endpoint de login tem 95 linhas e faz tudo que um endpoint de login deve fazer: verificação de método, rate limit em \u003ccode\u003engx.shared.fn_cache\u003c/code\u003e, comparação em tempo constante, verificação de senha PBKDF2, cookie de sessão. Aqui está a fatia do rate-limit:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/console/login_endpoint.lua:38-46\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-22\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eclient_ip\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.var\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eremote_addr\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;unknown\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003erate_store\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.shared\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efn_cache\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003erate_store\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003efail_count\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erate_store\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elogin_rate_key\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eclient_ip\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eLOGIN_MAX_ATTEMPTS\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efail_count\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e     \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003efail_count\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"n\"\u003eLOGIN_MAX_ATTEMPTS\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eguard.write_json\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e429\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;too many login attempts, try again later\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eCinco segundos de matemática de shared-dict e eu tenho um lockout funcional. \u003ccode\u003econsole/auth.lua\u003c/code\u003e (484 linhas) lida com o cookie de sessão, PBKDF2 com um mínimo de 100.000 iterações, segredos-de-arquivo opcionais para a senha do admin, e um TTL de 12 horas. \u003ccode\u003econsole/guard.lua\u003c/code\u003e (387 linhas) é o middleware que todo endpoint do dashboard chama primeiro para impor autenticação, limites de corpo, CSRF e write-gates. \u003ccode\u003econsole/data.lua\u003c/code\u003e é o grandão — cerca de 2.623 linhas — e é a \u0026ldquo;camada de serviço\u0026rdquo; de respaldo na qual os endpoints do dashboard delegam: listar funções, ler o código de uma função, definir código, listar versões, ler logs, ler agendamentos, agregar métricas do dashboard.\u003c/p\u003e\n\u003cp\u003eOs endpoints em si são minúsculos. O endpoint de métricas do dashboard tem 21 linhas:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003eopenresty/lua/fastfn/console/dashboard_endpoint.lua:9-21\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-23\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003engx.req\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_method\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e \u003cspan class=\"o\"\u003e~=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;GET\u0026#34;\u003c/span\u003e \u003cspan class=\"kr\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eguard.write_json\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e405\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;method not allowed\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003eend\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e-- Aggregate metrics from shared dicts or external systems\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003emetrics\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econsole.get_dashboard_metrics\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eguard.write_json\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003emetrics\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003einvocations\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003elatency\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eAo redor dele vivem \u003ccode\u003efunctions_endpoint.lua\u003c/code\u003e (34 linhas), \u003ccode\u003elogout_endpoint.lua\u003c/code\u003e (15 linhas), \u003ccode\u003eui_state_endpoint.lua\u003c/code\u003e (50 linhas), \u003ccode\u003esecrets_endpoint.lua\u003c/code\u003e (70 linhas), \u003ccode\u003epacks.lua\u003c/code\u003e (77 linhas), \u003ccode\u003eui.lua\u003c/code\u003e (66 linhas), e o mais gordinho \u003ccode\u003einvoke_endpoint.lua\u003c/code\u003e (526 linhas) que alimenta o console \u0026ldquo;invoque esta função a partir do navegador\u0026rdquo;. Não há aplicação web de admin separada. Não há \u003ccode\u003enpm run dev\u003c/code\u003e. Não há segundo sistema de build. Há Lua, renderizando HTML e JSON a partir do mesmo worker do nginx que serve o tráfego do gateway.\u003c/p\u003e\n\u003ch3 id=\"lua-como-runtime-e-um-cliente-http-muito-minúsculo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#lua-como-runtime-e-um-cliente-http-muito-min%c3%basculo\" class=\"header-mark\" aria-label=\"Header mark for 'Lua como runtime, e um cliente HTTP muito minúsculo'\"\u003e\u003c/a\u003e6.6 Lua como runtime, e um cliente HTTP muito minúsculo\u003c/h3\u003e\u003cp\u003eTambém adicionei \u003ccode\u003ecore/lua_runtime.lua\u003c/code\u003e (399 linhas), que permite que arquivos Lua no diretório de funções \u003cem\u003esejam\u003c/em\u003e handlers — mesmo contrato \u003ccode\u003ehandler(event) -\u0026gt; response\u003c/code\u003e que Python e Node, exceto que não há salto entre processos porque o handler roda no mesmo worker. Eu não publiquei um benchmark para este caminho, então não vou colocar um número de milissegundos nele. Para handlers que precisam alcançar para fora, \u003ccode\u003ecore/http_client.lua\u003c/code\u003e (356 linhas) envolve \u003ccode\u003engx.socket.tcp\u003c/code\u003e com parsing de URL, keepalive e timeouts, para que os handlers chamem APIs upstream sem puxar \u003ccode\u003eluasocket\u003c/code\u003e para dentro da imagem. \u003ccode\u003ecore/home.lua\u003c/code\u003e (234 linhas) serve a página de aterrissagem padrão em \u003ccode\u003e/\u003c/code\u003e, e \u003ccode\u003ehttp/function_code.lua\u003c/code\u003e / \u003ccode\u003ehttp/function_file_content.lua\u003c/code\u003e (50 e 76 linhas) fazem o ida-e-volta do código-fonte da função entre o editor do dashboard e o disco.\u003c/p\u003e\n\u003ch3 id=\"os-tradeoffs-honestos\" class=\"headerLink\"\u003e\n    \u003ca href=\"#os-tradeoffs-honestos\" class=\"header-mark\" aria-label=\"Header mark for 'Os tradeoffs honestos'\"\u003e\u003c/a\u003e6.7 Os tradeoffs honestos\u003c/h3\u003e\u003cp\u003eEntão — Lua é magnífica aqui? Sim, com comprovantes. Os comprovantes são:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eO cold start é essencialmente de graça.\u003c/strong\u003e O OpenResty inicializa uma vez. Meu código Lua é um conjunto de módulos carregados no init do worker. O arquivo \u003ccode\u003egateway.lua\u003c/code\u003e tem 1.341 linhas; carregá-lo adiciona praticamente nada à latência da requisição porque os hooks de fase da requisição já estão JIT-compilados quando a primeira requisição chega. Não tenho um número limpo de \u0026ldquo;tempo de carregamento do módulo Lua\u0026rdquo; para citar aqui, então não vou colocar uma cifra em milissegundos — mas todo \u003ccode\u003ep50\u003c/code\u003e de hot-path publicado no repositório, em todas as cargas, está nos milissegundos de um único dígito de ponta a ponta através deste gateway em Lua, e esse é o único número que importa para um usuário.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eA memória compartilhada é quase de graça.\u003c/strong\u003e \u003ccode\u003engx.shared.DICT\u003c/code\u003e é uma hash table no nginx, protegida por um spinlock. Eu não pago por um container Redis para guardar contadores.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eO módulo por preocupação é pequeno.\u003c/strong\u003e Tirando o core de descoberta de roteamento, a maioria dos módulos do plano de controle está na faixa de 100–500 linhas. \u003ccode\u003elimits.lua\u003c/code\u003e tem 133 linhas. \u003ccode\u003ewatchdog.lua\u003c/code\u003e tem 299. \u003ccode\u003elogin_endpoint.lua\u003c/code\u003e tem 95. Esses são números à distância de leitura. Posso segurar qualquer um deles na cabeça.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eE os custos, porque há custos:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eEsquisitices numéricas do Lua 5.1.\u003c/strong\u003e O LuaJIT do OpenResty é Lua-5.1-ish com algumas extensões 5.2/5.3. Não há \u003ccode\u003estring.pack\u003c/code\u003e nativo, que é por que o \u003ccode\u003epack_u32\u003c/code\u003e no Capítulo 4 existe. A confusão entre inteiro-versus-double me mordeu duas vezes enquanto escrevia o limitador de taxa; \u003ccode\u003engx.shared.DICT:incr\u003c/code\u003e retorna números que são doubles, e tive que ter cuidado com \u003ccode\u003emath.floor\u003c/code\u003e em contadores que eu planejava comparar contra valores \u003ccode\u003etonumber(env)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDisciplina de \u003ccode\u003epcall\u003c/code\u003e em todo lugar.\u003c/strong\u003e Um erro não capturado em um callback de \u003ccode\u003engx.timer\u003c/code\u003e mata o timer silenciosamente e loga no \u003ccode\u003eerror.log\u003c/code\u003e. Todo timer que escrevo está embrulhado em \u003ccode\u003epcall\u003c/code\u003e, e o tick do scheduler, o callback do watchdog e o escritor de persistência seguem todos o mesmo padrão. Pule essa disciplina e você ganha um scheduler que misteriosamente para de fazer tick às 3 da manhã.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTudo é uma string, até não ser.\u003c/strong\u003e Shared dicts guardam strings e números, não tabelas, o que significa encode/decode de JSON na fronteira. Eu me apoio em \u003ccode\u003ecjson.safe\u003c/code\u003e para que entrada ruim retorne \u003ccode\u003enil, err\u003c/code\u003e em vez de lançar, e ainda recebo o ocasional \u003ccode\u003ecannot encode sparse array\u003c/code\u003e quando uma tabela tem buracos numéricos.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNenhum desses é um impeditivo. Eles são o preço de escolher uma linguagem pequena com um JIT rápido embutido em um servidor web, e essa troca continua vencendo. O plano de controle inteiro — roteamento, OpenAPI, limites, watchdog, scheduler, console, auth — é um punhado de arquivos que posso ler em uma tarde. A maioria das preocupações é de 20 a 40 KB de Lua legível cada. O diretório \u003ccode\u003ecore/\u003c/code\u003e inteiro tem cerca de 304 KB no disco; o diretório \u003ccode\u003econsole/\u003c/code\u003e inteiro tem cerca de 156 KB. Isso é menos Lua, por uma margem ampla, do que a quantidade de JavaScript que um único SPA moderno entrega antes de chegar à primeira rota. E a Lua é \u003cem\u003eo produto\u003c/em\u003e, não o andaime.\u003c/p\u003e\n\u003cp\u003eO que me traz à parte em que tento puxar as lições deste monte.\u003c/p\u003e\n\u003ch2 id=\"capítulo-7-lições-que-só-esperava-pela-metade\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-7-li%c3%a7%c3%b5es-que-s%c3%b3-esperava-pela-metade\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 7: Lições que Só Esperava pela Metade'\"\u003e\u003c/a\u003e7 Capítulo 7: Lições que Só Esperava pela Metade\u003c/h2\u003e\u003ch3 id=\"sempre-coloque-o-env-do-host-em-allowlist\" class=\"headerLink\"\u003e\n    \u003ca href=\"#sempre-coloque-o-env-do-host-em-allowlist\" class=\"header-mark\" aria-label=\"Header mark for 'Sempre coloque o env do host em allowlist'\"\u003e\u003c/a\u003e7.1 Sempre coloque o env do host em allowlist\u003c/h3\u003e\u003cp\u003eTodo FaaS tem a mesma tentação: passar as variáveis de ambiente do host para dentro do handler. É conveniente. É também como segredos vazam. Se seu pipeline de CI/CD exporta \u003ccode\u003eAWS_SECRET_ACCESS_KEY\u003c/code\u003e para scripts de deploy, e seu daemon Python herda esse env para dentro de todo handler, parabéns, você acabou de entregar a todo handler na máquina uma credencial root da nuvem.\u003c/p\u003e\n\u003cp\u003eA correção é uma allowlist, não uma blocklist. Descobri isso do jeito chato: lendo relatórios de incidente de outras pessoas e decidindo que eu não queria escrever o meu próprio. O daemon vem com uma lista conservadora de chaves de env ambiente que os handlers podem ver (\u003ccode\u003esrv/fn/runtimes/python-daemon.py:59-103\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-24\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_ALLOWED_WORKER_ENV_KEYS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;PATH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;HOME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;USER\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;LOGNAME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# ... language runtimes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;PYTHONHOME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;PYTHONPATH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;GOPATH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;GOCACHE\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;CARGO_HOME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;RUSTUP_HOME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# ... CA bundles\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;SSL_CERT_FILE\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;SSL_CERT_DIR\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;REQUESTS_CA_BUNDLE\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# ... locale\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;LANG\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;TZ\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_ALLOWED_WORKER_ENV_PREFIXES\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;LC_\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO comentário acima dela diz a parte silenciosa em voz alta: \u003cem\u003e\u0026ldquo;User-defined secrets should come from fn.env.json or request-scoped event.env, not ambient host env.\u0026rdquo;\u003c/em\u003e A allowlist é a aplicação. Tudo que não está nesse conjunto, ou que começa com \u003ccode\u003eLC_\u003c/code\u003e, é limpo antes que o handler rode. É higiene de segurança, não teatro de segurança.\u003c/p\u003e\n\u003ch3 id=\"precedência-de-config-flag--env--config--padrão\" class=\"headerLink\"\u003e\n    \u003ca href=\"#preced%c3%aancia-de-config-flag--env--config--padr%c3%a3o\" class=\"header-mark\" aria-label=\"Header mark for 'Precedência de config: flag \u0026amp;gt; env \u0026amp;gt; config \u0026amp;gt; padrão'\"\u003e\u003c/a\u003e7.2 Precedência de config: flag \u0026gt; env \u0026gt; config \u0026gt; padrão\u003c/h3\u003e\u003cp\u003eEste é o tipo de regra que soa óbvia e mesmo assim está errada em metade das CLIs que já usei. Minha própria CLI acerta porque errei na primeira vez. De \u003ccode\u003ecli/cmd/run.go:64-73\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-25\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Resolve hot-reload: flag \u0026gt; env \u0026gt; config \u0026gt; default (true)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003ehotReload\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003erunHotReload\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Explicit --hot-reload flag always wins\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003ehotReload\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003eos\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eGetenv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;FN_HOT_RELOAD\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ehotReload\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;off\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003eenvVal\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;no\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nx\"\u003eviper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eIsSet\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;hot-reload\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ehotReload\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eviper\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eGetBool\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;hot-reload\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNote o comentário na linha 64. Ele não está lá para mim — está lá para a próxima pessoa que tentar \u0026ldquo;consertar\u0026rdquo; isto fazendo o arquivo de config ganhar do env. Todo sistema de config que já inverteu essa ordem causou um incidente em produção no qual alguém definiu uma variável de ambiente no CI, e então se perguntou por que o container continuava lendo um valor obsoleto de um arquivo commitado.\u003c/p\u003e\n\u003ch3 id=\"tudo-vive-no-sistema-de-arquivos\" class=\"headerLink\"\u003e\n    \u003ca href=\"#tudo-vive-no-sistema-de-arquivos\" class=\"header-mark\" aria-label=\"Header mark for 'Tudo vive no sistema de arquivos'\"\u003e\u003c/a\u003e7.3 Tudo vive no sistema de arquivos\u003c/h3\u003e\u003cp\u003eSem banco de dados. Sem registry. Sem etcd. As rotas são arquivos. A config é \u003ccode\u003efastfn.json\u003c/code\u003e. O env é \u003ccode\u003efn.env.json\u003c/code\u003e. Os overrides locais da função são \u003ccode\u003efn.config.json\u003c/code\u003e. O estado de dependência é \u003ccode\u003e.fastfn-deps-state.json\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eEsta decisão foi tomada por preguiça e acabou sendo um recurso. Um FaaS filesystem-first pode ser versionado, rsyncado, snapshotado e diffado com ferramentas que eu já tenho. Você não precisa de uma \u0026ldquo;plataforma\u0026rdquo; para inspecioná-lo. \u003ccode\u003els\u003c/code\u003e é a UI de admin. \u003ccode\u003egrep\u003c/code\u003e é o depurador. Esta é a mesma lição que o \u003ccode\u003einetd\u003c/code\u003e aprendeu em 1986: às vezes o plano de controle deveria ser um arquivo de texto.\u003c/p\u003e\n\u003ch2 id=\"capítulo-8-a-moral-da-história-parte-1\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cap%c3%adtulo-8-a-moral-da-hist%c3%b3ria-parte-1\" class=\"header-mark\" aria-label=\"Header mark for 'Capítulo 8: A Moral da História (Parte 1)'\"\u003e\u003c/a\u003e8 Capítulo 8: A Moral da História (Parte 1)\u003c/h2\u003e\u003cp\u003eEu me propus a construir \u0026ldquo;largue um arquivo Python, ganhe uma URL\u0026rdquo;. Acabei reconstruindo o FastCGI de propósito, com um gateway em Lua porque o OpenResty é genuinamente a ferramenta certa para este trabalho, um protocolo de fio em JSON porque formatos de registro binário são um mau tradeoff para um sistema local-first em 2026, e um contrato de runtime poliglota porque \u003ccode\u003edef handler(event): return {...}\u003c/code\u003e é a abstração certa e tem sido desde que o Lambda foi lançado.\u003c/p\u003e\n\u003cp\u003eA coisa que eu não esperava é quantas das decisões de design já tinham sido pré-feitas para mim pela história. O custo de fork-por-requisição que matou o CGI é o mesmo custo que faz o serverless container-por-requisição parecer lento hoje. O pool persistente + socket enquadrado do FastCGI é a mesma forma na qual as plataformas serverless modernas convergem internamente. Os Cloudflare Workers são, em sua essência, FastCGI com um isolado v8 em vez de um processo. Os pools de warm-start do AWS Lambda são FastCGI com um API Gateway na frente. \u003ccode\u003efastfn\u003c/code\u003e — pelo menos na forma que acabei de descrever — é FastCGI com frames JSON e um gateway OpenResty.\u003c/p\u003e\n\u003cp\u003eVou fechar a Parte 1 com o único arrependimento que de fato tenho: eu poderia ter começado com Lua um ano antes. Toda vez que escrevi \u0026ldquo;só um gatewayzinho em Go\u0026rdquo; eu estava escrevendo uma versão pior do que o nginx já faz, com um cold start maior, mais código e menos observabilidade. A lição — e não é uma lição nova, que é a parte constrangedora — é que quando a forma do seu problema combina com uma peça de infraestrutura existente e bem-amada, você deveria simplesmente usar essa infraestrutura. O FastCGI me disse como o data plane deveria parecer em 1996. O OpenResty me disse como o plano de controle deveria parecer em 2010. O Lambda me disse como o contrato de handler deveria parecer em 2014. Tudo que a Parte 1 do \u003ccode\u003efastfn\u003c/code\u003e fez foi escutar.\u003c/p\u003e\n\u003cp\u003eHá um segundo ramo desta história que este post deliberadamente não cobre: o que acontece quando uma função não é a forma certa — quando você precisa de um serviço de vida longa como um banco de dados, uma app Flask, ou um frontend Next.js vivendo dentro do mesmo gateway. Esse trabalho está atualmente em andamento em um branch de feature e ainda não foi publicado, então vou deixá-lo para a Parte 2, onde o isolamento de microVM Firecracker, a config \u003ccode\u003eapps\u003c/code\u003e / \u003ccode\u003eworkloads\u003c/code\u003e, e a rede vsock entre peers todos ganham o espaço de que precisam. As funções são a Parte 1. Os serviços são a Parte 2.\u003c/p\u003e\n\u003cdiv class=\"details admonition tip open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M96.06 454.35c.01 6.29 1.87 12.45 5.36 17.69l17.09 25.69a31.99 31.99 0 0 0 26.64 14.28h61.71a31.99 31.99 0 0 0 26.64-14.28l17.09-25.69a31.989 31.989 0 0 0 5.36-17.69l.04-38.35H96.01l.05 38.35zM0 176c0 44.37 16.45 84.85 43.56 115.78 16.52 18.85 42.36 58.23 52.21 91.45.04.26.07.52.11.78h160.24c.04-.26.07-.51.11-.78 9.85-33.22 35.69-72.6 52.21-91.45C335.55 260.85 352 220.37 352 176 352 78.61 272.91-.3 175.45 0 73.44.31 0 82.97 0 176zm176-80c-44.11 0-80 35.89-80 80 0 8.84-7.16 16-16 16s-16-7.16-16-16c0-61.76 50.24-112 112-112 8.84 0 16 7.16 16 16s-7.16 16-16 16z\"/\u003e\u003c/svg\u003e\u003c/span\u003eContinue lendo\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003eParte 2 — \u003ca href=\"https://misael.org/pt/fastfn-services-when-functions-arent-enough/\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003cstrong\u003efastfn Parte 2: Quando uma Função Não é Suficiente\u003c/strong\u003e\u003c/a\u003e — retoma onde este post termina: serviços, workloads, microVMs Firecracker, e a rede vsock que permite a uma função alcançar uma VM Postgres em \u003ccode\u003epostgres.internal\u003c/code\u003e sem nunca expor uma porta no host.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n",
        "language": "pt"
    },
    {
        "title" : "Ensinando um Teclado Gigabyte a Falar Linux",
        "date_published" : "2026-05-26T00:00:00Z",
        "date_modified" : "2026-05-26T00:00:00Z",
        "id" : "https://misael.org/pt/teaching-a-gigabyte-keyboard-to-speak-linux/",
        "url" : "https://misael.org/pt/teaching-a-gigabyte-keyboard-to-speak-linux/",
        "summary": "Um teclado RGB Gigabyte Aero, /dev/hidraw, um checksum não-XOR e uma regra udev que transforma \u0026lsquo;sudo toda vez\u0026rsquo; em \u0026lsquo;simplesmente funciona\u0026rsquo;.",
        "content_html" : "\u003ch2 id=\"a-dor-de-um-teclado-escuro\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-dor-de-um-teclado-escuro\" class=\"header-mark\" aria-label=\"Header mark for 'A Dor de um Teclado Escuro'\"\u003e\u003c/a\u003e1 A Dor de um Teclado Escuro\u003c/h2\u003e\u003cp\u003eComprei um Gigabyte Aero X16 com toda a intenção de torná-lo meu \u0026ldquo;computador do dia a dia que por acaso também roda builds sem chorar\u0026rdquo;. No Windows, a primeira coisa que aconteceu quando o liguei foi teatral: o teclado se acendeu numa onda de ciano, como uma pequena banda marcial de LEDs, e o Gigabyte Control Center insistia alegremente para que eu escolhesse entre uma dúzia de efeitos RGB. Parecia um teclado que queria conversar comigo.\u003c/p\u003e\n\u003cp\u003eEntão instalei o Linux, e o teclado se calou. Nem um lampejo. Nem um brilho. Nada.\u003c/p\u003e\n\u003cp\u003ePara ser justo, a máquina ainda digitava. As teclas ainda produziam caracteres. Mas existe um tipo particular de dor peculiar aos desenvolvedores que pagaram por RGB e depois passaram uma noite encarando plástico apagado. A dor cutucou um pensamento: se o hardware ainda funciona e o sistema operacional é a única coisa que mudou, então em algum lugar entre minha sessão de usuário e o firmware do teclado existe uma conversa que simplesmente não está acontecendo — e conversas que não estão acontecendo podem, em princípio, ser iniciadas. Olhei para as ferramentas existentes: há um projeto maravilhoso para Windows chamado \u003ca href=\"https://gitlab.com/wtwrp/aeroctl\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003ewtwrp/aeroctl\u003c/code\u003e\u003c/a\u003e que faz tudo o que alguém poderia querer, desde que essa pessoa esteja disposta a também rodar o Windows. Eu não estava. Então fiz o que se faz: abri um terminal, resmunguei algo impublicável sobre o \u003ccode\u003elsusb\u003c/code\u003e e comecei a cutucar.\u003c/p\u003e\n\u003cp\u003eAlgumas noites depois, eu tinha um pequeno projeto em Python que chamei de \u003ca href=\"https://github.com/misaelzapata/aeroctl-linux-indicator\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003eaeroctl-linux-indicator\u003c/code\u003e\u003c/a\u003e. São 593 linhas de código distribuídas pelo pacote (\u003ccode\u003edevice.py\u003c/code\u003e + \u003ccode\u003ecli.py\u003c/code\u003e + \u003ccode\u003etray.py\u003c/code\u003e), está coberto por testes em cerca de 80%, traz uma CLI e um indicador de bandeja, e seu propósito inteiro é convencer um controlador ITE-829X enterrado dentro de um chassi Gigabyte de que o Linux é, de fato, um sistema operacional legítimo merecedor de luz colorida.\u003c/p\u003e\n\u003cp\u003eEsta é a história de como cheguei lá. É também a história de como um commit — \u003ccode\u003e68dbdae\u003c/code\u003e, \u0026ldquo;chore: remove unsupported RGB effects\u0026rdquo; — é a linha mais triste do repositório.\u003c/p\u003e\n\u003cp\u003eEsse commit, no entanto, é o fim do fio. O começo dele é uma pergunta mais inocente — uma que só fiz \u003cem\u003edepois\u003c/em\u003e de algumas noites de decepção com as respostas dos outros: o que está realmente plugado no barramento USB com que eu ainda não conversei, e como eu sequer saberia?\u003c/p\u003e\n\u003ch2 id=\"a-arte-anterior-quatro-projetos-nenhum-para-este-laptop\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-arte-anterior-quatro-projetos-nenhum-para-este-laptop\" class=\"header-mark\" aria-label=\"Header mark for 'A Arte Anterior: Quatro Projetos, Nenhum Para Este Laptop'\"\u003e\u003c/a\u003e2 A Arte Anterior: Quatro Projetos, Nenhum Para Este Laptop\u003c/h2\u003e\u003cp\u003eAntes de escrever uma única linha de Python, fiz aquilo que todo engenheiro deveria fazer e fui procurar trabalho existente. A notícia decepcionante-barra-encorajadora é que existe bastante arte anterior sobre \u0026ldquo;RGB de teclado de laptop Gigabyte a partir do Linux\u0026rdquo;. A parte decepcionante é que nenhuma delas funcionava no \u003cem\u003emeu\u003c/em\u003e laptop específico sem ajustes. A parte encorajadora é que cada uma delas me ensinou algo que acabei usando.\u003c/p\u003e\n\u003cp\u003eAqui está a árvore que percorri antes de me afastar dela.\u003c/p\u003e\n\u003ch3 id=\"wtwrpaeroctl--o-nome-que-peguei-emprestado\" class=\"headerLink\"\u003e\n    \u003ca href=\"#wtwrpaeroctl--o-nome-que-peguei-emprestado\" class=\"header-mark\" aria-label=\"Header mark for 'wtwrp/aeroctl — o nome que peguei emprestado'\"\u003e\u003c/a\u003e2.1 wtwrp/aeroctl — o nome que peguei emprestado\u003c/h3\u003e\u003cp\u003e\u003ca href=\"https://gitlab.com/wtwrp/aeroctl\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003ewtwrp/aeroctl\u003c/code\u003e\u003c/a\u003e é o AeroCtl original, uma aplicação C# / .NET para Windows. É o projeto que deu o nome ao meu, e lê-lo é a maneira mais fácil de entender como um Gigabyte Aero bem suportado se parece do lado do host: controle de ventoinha, política de carregamento, teclas Fn não-padronizadas, RGB do teclado, GPU boost, tudo passando pelo driver ACPI WMI da Gigabyte (\u003ccode\u003eacpimof.dll\u003c/code\u003e e companhia). A seção de RGB em particular é informativa: ela confirma que o teclado é alcançado por meio de um HID feature report e não por meio de alguma porta de IO proprietária ou blob de WMI.\u003c/p\u003e\n\u003cp\u003eO problema é óbvio: é Windows. A premissa inteira é que você já instalou o Gigabyte ControlCenter ou o SmartManager, de modo que \u003ccode\u003eacpimof.dll\u003c/code\u003e esteja presente. Nada disso existe no meu laptop, que roda Linux exclusivamente. Mas o código de exemplo de RGB em \u003ccode\u003eSamples/\u003c/code\u003e — a parte que é apenas \u0026ldquo;abra o dispositivo HID, escreva um feature report, feche\u0026rdquo; — é portável em espírito. Essa é a peça que carreguei comigo.\u003c/p\u003e\n\u003ch3 id=\"aerocontrolcenter--a-port-para-linux-que-pediu-um-driver-de-kernel\" class=\"headerLink\"\u003e\n    \u003ca href=\"#aerocontrolcenter--a-port-para-linux-que-pediu-um-driver-de-kernel\" class=\"header-mark\" aria-label=\"Header mark for 'AeroControlCenter — a port para Linux que pediu um driver de kernel'\"\u003e\u003c/a\u003e2.2 AeroControlCenter — a port para Linux que pediu um driver de kernel\u003c/h3\u003e\u003cp\u003e\u003ca href=\"https://github.com/tangalbert919/AeroControlCenter\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003etangalbert919/AeroControlCenter\u003c/code\u003e\u003c/a\u003e é uma port para Linux do Gigabyte Control Center, escrita em C++/Qt, voltada ao Aero 15 Classic (SA/WA/XA/YA). Sua abordagem é mais ambiciosa que a minha: ela quer lidar com controle de ventoinha, política de bateria \u003cem\u003ee\u003c/em\u003e RGB, e para fazer isso de forma limpa depende de um driver de kernel companheiro, \u003ca href=\"https://github.com/tangalbert919/gigabyte-laptop-wmi\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003egigabyte-laptop-wmi\u003c/code\u003e\u003c/a\u003e, que expõe a interface WMI como nós sysfs do Linux.\u003c/p\u003e\n\u003cp\u003eAdmiro a arquitetura e não pude usá-la. Minha máquina não está na lista testada, \u003ccode\u003egigabyte-laptop-wmi\u003c/code\u003e não dá bind de forma limpa em kernels modernos no meu chassi, e eu não tinha o apetite para depurar uma tabela WMI ACPI num laptop que eu também precisava manter funcionando para e-mail. O que o AeroControlCenter \u003cem\u003eme deu\u003c/em\u003e, porém, foi o arquivo \u003ccode\u003e70-keyboard.rules\u003c/code\u003e — ou seja, a confirmação de que a abordagem via udev era o formato certo para o problema de permissões. Escrevi minha própria regra, mas a escrevi porque a regra daquele projeto me disse onde colocá-la.\u003c/p\u003e\n\u003ch3 id=\"fusion-kbd-controller--libusb-unbind-do-kernel-e-um-aviso\" class=\"headerLink\"\u003e\n    \u003ca href=\"#fusion-kbd-controller--libusb-unbind-do-kernel-e-um-aviso\" class=\"header-mark\" aria-label=\"Header mark for 'fusion-kbd-controller — libusb, unbind do kernel e um aviso'\"\u003e\u003c/a\u003e2.3 fusion-kbd-controller — libusb, unbind do kernel e um aviso\u003c/h3\u003e\u003cp\u003e\u003ca href=\"https://github.com/martin31821/fusion-kbd-controller\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003emartin31821/fusion-kbd-controller\u003c/code\u003e\u003c/a\u003e é um pequeno binário userspace em C usando libusb, voltado ao AERO 15X. Ler o README é uma viagem: precisa de root, faz unbind temporário do dispositivo USB do driver \u003ccode\u003eusbhid\u003c/code\u003e do kernel, e o autor alegremente avisa que \u0026ldquo;é possível inutilizar seu teclado ao enviar valores absurdos aqui\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eFunciona, num 15X, com libusb, com detach do kernel. Eu não queria nenhuma dessas coisas. Eu queria \u003ccode\u003e/dev/hidraw\u003c/code\u003e para poder conversar com o dispositivo enquanto o kernel continuava sendo dono da interface HID (de modo que as teclas continuassem digitando enquanto eu enviava comandos de cor), eu queria uma regra udev para não precisar de root, e eu \u003cem\u003erealmente\u003c/em\u003e não queria inutilizar meu próprio teclado descobrindo o que \u0026ldquo;valores absurdos\u0026rdquo; significava para o hardware de outra pessoa. Mas o fusion-kbd-controller é onde peguei o modelo mental de \u0026ldquo;pacote de bytes com um modo e uma região de parâmetros\u0026rdquo;, que acabou se generalizando para o pacote de feature report do ITE-829X que acabei escrevendo.\u003c/p\u003e\n\u003ch3 id=\"keyboard-fusion-rgb--linguagem-certa-fabricante-errado\" class=\"headerLink\"\u003e\n    \u003ca href=\"#keyboard-fusion-rgb--linguagem-certa-fabricante-errado\" class=\"header-mark\" aria-label=\"Header mark for 'keyboard-fusion-rgb — linguagem certa, fabricante errado'\"\u003e\u003c/a\u003e2.4 keyboard-fusion-rgb — linguagem certa, fabricante errado\u003c/h3\u003e\u003cp\u003e\u003ca href=\"https://github.com/rcassani/keyboard-fusion-rgb\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003ercassani/keyboard-fusion-rgb\u003c/code\u003e\u003c/a\u003e é um driver HIDAPI em Python para o AORUS 15G. É a coisa mais próxima do \u0026ldquo;que acabei fazendo\u0026rdquo; — Python, HID, userspace, com engenharia reversa a partir do AORUS Control Center do Windows — exceto por um detalhe: os IDs de fabricante/produto são \u003ccode\u003e0x1044:0x7a3c\u003c/code\u003e (Chu Yuen Enterprise Co., Ltd.), que é uma família de controlador de teclado completamente diferente da do meu laptop (\u003ccode\u003e0x0414:0x8104\u003c/code\u003e, o ITE-829X).\u003c/p\u003e\n\u003cp\u003eEntão o código não pode ser usado como está — o formato de fio é diferente, o layout por tecla é diferente, os IDs de modo são diferentes. Mas a \u003cem\u003eabordagem\u003c/em\u003e é exatamente a que eu queria: feature reports no estilo HIDAPI a partir do Python, decodificados observando o que a ferramenta do Windows escreve. A existência daquele projeto também foi um empurrão moral: se o \u003ccode\u003ercassani\u003c/code\u003e conseguiu decodificar uma subfamília Gigabyte observando o tráfego USB, eu poderia decodificar a do meu laptop da mesma forma.\u003c/p\u003e\n\u003ch3 id=\"openrgb--o-gigante-que-ainda-não-conhece-esta-máquina\" class=\"headerLink\"\u003e\n    \u003ca href=\"#openrgb--o-gigante-que-ainda-n%c3%a3o-conhece-esta-m%c3%a1quina\" class=\"header-mark\" aria-label=\"Header mark for 'OpenRGB — o gigante que ainda não conhece esta máquina'\"\u003e\u003c/a\u003e2.5 OpenRGB — o gigante que ainda não conhece esta máquina\u003c/h3\u003e\u003cp\u003e\u003ca href=\"https://gitlab.com/CalcProgrammer1/OpenRGB\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\u003ccode\u003eCalcProgrammer1/OpenRGB\u003c/code\u003e\u003c/a\u003e é a ferramenta RGB universal — Windows, Linux, macOS, toda placa-mãe e fabricante de RAM debaixo do sol. É o projeto que eu queria poder usar e não pude. Sua \u003ca href=\"https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues/2288\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eissue aberta #2288\u003c/a\u003e sobre o Gigabyte Aero 15 OLED YD é, essencialmente, \u0026ldquo;ainda não temos este modelo\u0026rdquo;. Meu laptop é um SKU diferente daquela issue, mas próximo o bastante para eu saber que a resposta era a mesma: ainda não chegou.\u003c/p\u003e\n\u003cp\u003eMenciono o OpenRGB explicitamente porque num mundo melhor este post não existe — eu teria aberto o painel de configurações deles, escolhido um efeito e seguido em frente. Em vez disso, vou decodificar um pacote, escrever 593 linhas de Python e fingir que estou bem com isso.\u003c/p\u003e\n\u003ch3 id=\"o-que-peguei-da-árvore\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-que-peguei-da-%c3%a1rvore\" class=\"header-mark\" aria-label=\"Header mark for 'O que peguei da árvore'\"\u003e\u003c/a\u003e2.6 O que peguei da árvore\u003c/h3\u003e\u003cp\u003eO resumo de uma hora de pesquisa ficou mais ou menos assim:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-1\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  prior art                         usable for my laptop?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ----------------------------      ---------------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  wtwrp/aeroctl (Windows, C#)       no  (wrong OS, WMI-based)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    yes — packet shape intuition\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  AeroControlCenter (Linux, Qt)     no  (different model; wants kernel driver)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    yes — udev rule as the permissions story\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  fusion-kbd-controller (C, libusb) no  (wrong model; kernel unbind; root only)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    yes — \u0026#34;mode byte + params + checksum\u0026#34; mental model\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  keyboard-fusion-rgb (Py, HIDAPI)  no  (different vendor, 0x1044 vs 0x0414)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    yes — Python + HID is the right shape\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  OpenRGB (C++, everywhere)         no  (my SKU not yet supported)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                    yes — confirmation that nobody else had this\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO que é o truque da pesquisa de arte anterior neste canto do mundo: a resposta para \u0026ldquo;existe algo para a minha máquina\u0026rdquo; é quase sempre \u003cem\u003enão\u003c/em\u003e, mas a resposta para \u0026ldquo;o que outras pessoas já descobriram que eu não deveria re-derivar\u0026rdquo; é quase sempre \u003cem\u003ealguma coisa\u003c/em\u003e. Fechei as abas do navegador com um plano meio formado: Python, \u003ccode\u003e/dev/hidraw\u003c/code\u003e, uma regra udev para não precisar de sudo, um pacote que provavelmente é \u0026ldquo;modo + params + checksum8\u0026rdquo; e — o grande risco — um algoritmo de checksum que eu teria de adivinhar porque nenhuma das artes anteriores usa exatamente este controlador.\u003c/p\u003e\n\u003cp\u003eO que me trouxe de volta à pergunta inocente: o que está \u003cem\u003erealmente\u003c/em\u003e no meu barramento USB, e o que ele quer ouvir?\u003c/p\u003e\n\u003ch2 id=\"fazendo-engenharia-reversa-do-teclado\" class=\"headerLink\"\u003e\n    \u003ca href=\"#fazendo-engenharia-reversa-do-teclado\" class=\"header-mark\" aria-label=\"Header mark for 'Fazendo Engenharia Reversa do Teclado'\"\u003e\u003c/a\u003e3 Fazendo Engenharia Reversa do Teclado\u003c/h2\u003e\u003cp\u003eA primeira coisa que aprendi é que fazer engenharia reversa de um periférico USB é 10% de trabalho detetivesco esperto e 90% de leitura da saída do \u003ccode\u003edmesg\u003c/code\u003e enquanto sua esposa pergunta o que você está fazendo à 1h da manhã. O teclado do Gigabyte Aero não é um dongle separado, claro — é um dispositivo USB interno, e o \u003ccode\u003elsusb | grep 0414\u003c/code\u003e gentilmente confirmou isso:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-2\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBus 001 Device 003: ID 0414:8104 Gigabyte Technology Co.,Ltd ...\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eFabricante \u003ccode\u003e0x0414\u003c/code\u003e, produto \u003ccode\u003e0x8104\u003c/code\u003e. Esse é o ITE-829X. Googlei tudo o que consegui encontrar, fiz referência cruzada com os fontes da ferramenta do Windows e formei uma hipótese: o controle RGB vive atrás de um HID feature report, exposto numa interface cuja usage page é \u003ccode\u003e0xFF01\u003c/code\u003e — uma página HID definida pelo fabricante, que é o equivalente USB de uma estrada de terra marcada como \u0026ldquo;privada\u0026rdquo;.\u003c/p\u003e\n\u003ch3 id=\"encontrando-o-devhidraw-certo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#encontrando-o-devhidraw-certo\" class=\"header-mark\" aria-label=\"Header mark for 'Encontrando o \u0026lt;code\u0026gt;/dev/hidraw\u0026lt;/code\u0026gt; certo'\"\u003e\u003c/a\u003e3.1 Encontrando o \u003ccode\u003e/dev/hidraw\u003c/code\u003e certo\u003c/h3\u003e\u003cp\u003eAqui é onde a engenharia reversa para de parecer mágica e começa a parecer arqueologia de sistema de arquivos. O Linux expõe todo dispositivo HID em \u003ccode\u003e/sys/bus/hid/devices/\u003c/code\u003e, e para cada dispositivo ele te entrega um \u003ccode\u003ereport_descriptor\u003c/code\u003e — um blob de bytes que te diz (se você apertar os olhos) sobre o que o dispositivo está disposto a conversar.\u003c/p\u003e\n\u003cp\u003eEscrevi um scanner. Dado o par fabricante/produto certo, ele faz glob no diretório sysfs, lê cada \u003ccode\u003ereport_descriptor\u003c/code\u003e e procura pelos três bytes \u003ccode\u003e\\x06\\x01\\xff\u003c/code\u003e — HID-ês para \u0026ldquo;usage page \u003ccode\u003e0xFF01\u003c/code\u003e\u0026rdquo;. Quando encontro uma correspondência, sigo o symlink até seu subdiretório \u003ccode\u003ehidraw\u003c/code\u003e e pego o nó \u003ccode\u003e/dev/hidrawN\u003c/code\u003e que pertence a ele.\u003c/p\u003e\n\u003cp\u003ePense no udev + sysfs aqui como uma espécie de \u003ccode\u003e/proc\u003c/code\u003e para periféricos: um documento vivo e somente-leitura que descreve o que o kernel atualmente acredita sobre cada dispositivo plugado. E, assim como \u003ccode\u003e/proc\u003c/code\u003e, a forma correta de usá-lo não é confiar em nenhum número de dispositivo em particular — \u003ccode\u003e/dev/hidraw0\u003c/code\u003e hoje pode ser \u003ccode\u003e/dev/hidraw3\u003c/code\u003e amanhã se você plugar um mouse — mas escanear, filtrar e escolher por propriedade.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-3\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/device.py:99-119\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@staticmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_find_hidraw\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evendor_id\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eVID_DEFAULT\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eproduct_id\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003ePID_DEFAULT\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eusage_page_bytes\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"sa\"\u003eb\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x06\\x01\\xff\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Find the hidraw node with Usage Page 0xFF01 by scanning /sys.\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003epattern\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/sys/bus/hid/devices/0003:\u003c/span\u003e\u003cspan class=\"si\"\u003e%04X\u003c/span\u003e\u003cspan class=\"s2\"\u003e:\u003c/span\u003e\u003cspan class=\"si\"\u003e%04X\u003c/span\u003e\u003cspan class=\"s2\"\u003e.*\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003evendor_id\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eproduct_id\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003epath\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003esorted\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eglob\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eglob\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e)):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003erd_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;report_descriptor\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"nb\"\u003eopen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erd_path\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;rb\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003erd\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ef\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eOSError\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eusage_page_bytes\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003erd\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ehidraw_dir\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;hidraw\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eentries\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elistdir\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehidraw_dir\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eentries\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003edevpath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/dev/\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003eentries\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edevpath\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003edevpath\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eOSError\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO prefixo \u003ccode\u003e0003:\u003c/code\u003e no padrão de glob merece uma pausa. Dispositivos HID são registrados pelo kernel com IDs de barramento, e \u003ccode\u003e0003\u003c/code\u003e é USB. O padrão completo se expande, para a minha máquina, em \u003ccode\u003e/sys/bus/hid/devices/0003:0414:8104.*\u003c/code\u003e. O \u003ccode\u003e*\u003c/code\u003e está ali porque um único dispositivo físico pode expor várias interfaces HID — uma para as teclas normais, uma para as teclas de mídia, uma para o canal de controle do fabricante — e eu quero aquela cujo report descriptor anuncia \u003ccode\u003e0xFF01\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAqui está o fluxo de descoberta com que acabei:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-4\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 ┌──────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  /sys/bus/hid/devices/0003:0414:8104.*   │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  glob-expand (one entry per HID iface)   │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 └─────────────────────┬────────────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 ┌──────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  open report_descriptor, scan for        │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  b\u0026#39;\\x06\\x01\\xff\u0026#39;  ==  usage page 0xFF01  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 └─────────────────────┬────────────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       │ match\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 ┌──────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  ls \u0026lt;iface\u0026gt;/hidraw/  -\u0026gt;  \u0026#34;hidraw0\u0026#34;       │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  devpath = \u0026#34;/dev/\u0026#34; + entry               │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 └─────────────────────┬────────────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                       ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 ┌──────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  os.open(devpath, O_RDWR)                │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 │  fcntl.ioctl(fd, HIDIOCSFEATURE(9), buf) │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                 └──────────────────────────────────────────┘\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eUma vez que esse caminho do hidraw está em mãos, todo o resto é \u003ccode\u003efcntl.ioctl\u003c/code\u003e. Os ioctls \u003ccode\u003eHIDIOCSFEATURE\u003c/code\u003e e \u003ccode\u003eHIDIOCGFEATURE\u003c/code\u003e são a maneira padrão de enviar e receber HID feature reports a partir do userspace. Construí os números mágicos à mão porque não queria depender do \u003ccode\u003ehidapi\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-5\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/device.py:56-62\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_ioc\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edirection\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etyp\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003enr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edirection\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eord\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etyp\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"n\"\u003enr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHIDIOCSFEATURE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003elambda\u003c/span\u003e \u003cspan class=\"n\"\u003en\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003e_ioc\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;H\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x06\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003en\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHIDIOCGFEATURE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003elambda\u003c/span\u003e \u003cspan class=\"n\"\u003en\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003e_ioc\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;H\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x07\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003en\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eREPORT_SIZE\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA classe \u003ccode\u003eAeroKeyboardRGB\u003c/code\u003e (\u003ccode\u003esrc/aeroctl_linux_indicator/device.py:77-240\u003c/code\u003e) é toda a abstração de hardware num só lugar: abre o dispositivo, escreve pacotes de 9 bytes, lê respostas de 9 bytes, fecha ao sair. Ela também é um context manager, porque gosto dos meus file descriptors do jeito que gosto dos meus laptops caros: fechados quando terminei com eles.\u003c/p\u003e\n\u003cp\u003eTer um file descriptor é uma coisa. Ter os \u003cem\u003ebytes certos para enviar por ele\u003c/em\u003e é outra, e é aí que bati na primeira parede que parecia um quebra-cabeça em vez de um problema de encanamento — o que, acontece, é exatamente quando a engenharia reversa fica interessante.\u003c/p\u003e\n\u003ch2 id=\"o-pacote-nove-bytes-e-um-checksum-não-xor\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-pacote-nove-bytes-e-um-checksum-n%c3%a3o-xor\" class=\"header-mark\" aria-label=\"Header mark for 'O Pacote: Nove Bytes e um Checksum Não-XOR'\"\u003e\u003c/a\u003e4 O Pacote: Nove Bytes e um Checksum Não-XOR\u003c/h2\u003e\u003cp\u003eAgora, o pacote. Esta é a parte engraçada.\u003c/p\u003e\n\u003cp\u003eVocê poderia esperar — eu certamente esperava — que um modesto pacote de controle de 9 bytes para um controlador de LED se verificasse com XOR. Todo mundo usa XOR. XOR é barato, XOR é o que o engenheiro de firmware busca quando são 16h de uma sexta-feira e ele quer ir para casa. Encarei capturas do Wireshark da ferramenta do Windows. Escrevi um pequeno script em Python para tentar XOR sobre cada fatia possível do pacote. Nada disso correspondeu.\u003c/p\u003e\n\u003cp\u003eTentei a soma simples. Também não.\u003c/p\u003e\n\u003cp\u003eTentei \u003ccode\u003e256 - sum\u003c/code\u003e. Perto. Suspeitosamente perto. Errado por um.\u003c/p\u003e\n\u003cp\u003eA resposta, acontece, é esta:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-6\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003echecksum = (0xFF - (sum(bytes[1..7]) \u0026amp; 0xFF)) \u0026amp; 0xFF\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNão XOR. \u003cstrong\u003eNão XOR.\u003c/strong\u003e É uma coisa meio complemento-de-um de 8 bits: pegue a soma dos bytes de payload, mascare para um byte e depois subtraia de \u003ccode\u003e0xFF\u003c/code\u003e. Um checksum do tipo \u0026ldquo;fazer o total inteiro dar \u003ccode\u003e0xFF\u003c/code\u003e\u0026rdquo;. O que é perfeitamente razoável, no jeito em que todos os protocolos de hardware são perfeitamente razoáveis depois que você para de esperar que sejam.\u003c/p\u003e\n\u003cp\u003eSe você está pensando \u0026ldquo;ah, isso é só \u003ccode\u003e~sum + 1 - 1\u003c/code\u003e, não é?\u0026rdquo; — sim, e também, não, pare com isso, não é um complemento de dois. É um invariante \u0026ldquo;soma mais checksum deve dar \u003ccode\u003e0xFF\u003c/code\u003e\u0026rdquo;. O que é elegante, se ligeiramente incomum, e a primeira regra da engenharia reversa é: faça o que o hardware faz, mesmo que o hardware seja um pouco excêntrico.\u003c/p\u003e\n\u003cp\u003eAqui está a implementação inteira:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-7\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/device.py:94-96\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@staticmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_checksum8\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mh\"\u003e0xFF\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003esum\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xFF\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xFF\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDuas linhas, mais um decorator. O \u003ccode\u003e\u0026amp; 0xFF\u003c/code\u003e na parte de fora é cinto-e-suspensório — aritmética em Python é ilimitada, e eu quero que isso caiba num byte independentemente de quantos bytes eu passar.\u003c/p\u003e\n\u003cp\u003eO layout do pacote em si é este:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-8\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  Offset  ┬───────────────────────────────────────────────────────────\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B0     │  0x00                        ← HID report ID / padding\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B1     │  command   (e.g. 0x08 = SET_EFFECT, 0x80 = GET_FIRMWARE)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B2     │  sub-arg   (often 0x00)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B3     │  effect id (1=static, 2=breathing, 3=wave, ...)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B4     │  speed\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B5     │  brightness\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B6     │  color id  (0..8)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B7     │  direction\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   B8     │  checksum  =  (0xFF - (sum(B0..B7) \u0026amp; 0xFF)) \u0026amp; 0xFF\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ────────┴───────────────────────────────────────────────────────────\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e           9 bytes total, sent via HIDIOCSFEATURE ioctl on /dev/hidrawN\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA escolha de framing merece ser admirada por um momento. O byte 0 é o HID report ID, e como este dispositivo em particular usa o report ID \u003ccode\u003e0\u003c/code\u003e, ele é efetivamente padding — mas a ABI \u003ccode\u003ehidraw\u003c/code\u003e do Linux sempre quer um report ID como o primeiro byte de um feature report, então eu o carrego junto. Os bytes 1–7 são onde o comando de fato vive. O byte 8 é o checksum sobre o qual passei duas horas estando errado.\u003c/p\u003e\n\u003cp\u003eSe você já escreveu emuladores de terminal, a analogia que encaixou para mim foi esta: \u003cstrong\u003eum pacote de HID feature report é basicamente um código de escape ANSI com um checksum.\u003c/strong\u003e Você tem um introdutor (o byte de comando), você tem parâmetros (efeito, velocidade, brilho, cor, direção) e você tem um terminador — exceto que o terminador, em vez de ser uma letra imprimível como \u003ccode\u003em\u003c/code\u003e para \u0026ldquo;definir modo gráfico\u0026rdquo;, é um byte de checksum que verifica se o firmware não recebeu lixo. Envie o byte errado e o controlador educadamente o ignora. Envie os bytes certos e seu teclado vira uma discoteca.\u003c/p\u003e\n\u003cp\u003eO resto de \u003ccode\u003edevice.py\u003c/code\u003e é só vocabulário. \u003ccode\u003eset_effect\u003c/code\u003e é \u003ccode\u003e_send_feature(_packet(0x08, 0x00, effect, speed, brightness, color, direction))\u003c/code\u003e. \u003ccode\u003eget_firmware_version\u003c/code\u003e é \u003ccode\u003e_send_feature(_packet(0x80))\u003c/code\u003e seguido de uma leitura \u003ccode\u003e_get_feature(9)\u003c/code\u003e. \u003ccode\u003ereset\u003c/code\u003e é \u003ccode\u003e_send_feature(_packet(0x13, 0xFF))\u003c/code\u003e. Cada comando é uma linha. Cada linha é uma frase numa língua que eu acabei aprendendo a falar.\u003c/p\u003e\n\u003cp\u003eFalar essa língua corretamente, no entanto, pressupunha que eu sequer conseguisse abrir o file descriptor em primeiro lugar — e depois que digitei minha senha do sudo pela quadragésima vez num dia, o próximo galho da árvore basicamente se anunciou.\u003c/p\u003e\n\u003ch2 id=\"udev-o-guardião-da-felicidade\" class=\"headerLink\"\u003e\n    \u003ca href=\"#udev-o-guardi%c3%a3o-da-felicidade\" class=\"header-mark\" aria-label=\"Header mark for 'udev: O Guardião da Felicidade'\"\u003e\u003c/a\u003e5 udev: O Guardião da Felicidade\u003c/h2\u003e\u003cp\u003eHá um círculo especial do purgatório do desktop Linux reservado para ferramentas que funcionam perfeitamente, mas só se você as roda com \u003ccode\u003esudo\u003c/code\u003e. Toda vez que eu alternava o teclado, eu tinha de digitar minha senha. Toda vez que o indicador de bandeja queria ler o efeito atual, ele não conseguia, porque um processo de usuário normal não pode abrir \u003ccode\u003e/dev/hidraw0\u003c/code\u003e para leitura-escrita por padrão. Isso era um impedimento fatal para um \u0026ldquo;indicador de bandeja\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eA correção é uma regra \u003ccode\u003eudev\u003c/code\u003e de quatro linhas, e é uma pequena obra-prima:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ebash\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-9\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# install.sh:14-21\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eUDEV_RULE_FILE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/etc/udev/rules.d/70-gigabyte-kbd.rules\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e ! -f \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$UDEV_RULE_FILE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;SUBSYSTEMS==\u0026#34;usb\u0026#34;, ATTRS{idVendor}==\u0026#34;0414\u0026#34;, ATTRS{idProduct}==\u0026#34;8104\u0026#34;, MODE=\u0026#34;0660\u0026#34;, TAG+=\u0026#34;uaccess\u0026#34;\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e|\u003c/span\u003e sudo tee \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$UDEV_RULE_FILE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    sudo udevadm control --reload-rules \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e sudo udevadm trigger\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;udev rules created and applied.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;udev rules already exist.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efi\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA palavra mágica aqui é \u003ccode\u003eTAG+=\u0026quot;uaccess\u0026quot;\u003c/code\u003e. Essa tag diz ao systemd-logind: \u0026ldquo;quem quer que esteja atualmente logado no seat local deveria ter permissão para acessar este dispositivo\u0026rdquo;. Sem ela, \u003ccode\u003e/dev/hidraw0\u003c/code\u003e pertence a \u003ccode\u003eroot:root\u003c/code\u003e no modo \u003ccode\u003e0660\u003c/code\u003e, e seu processo de usuário ricocheteia nele como uma mariposa numa janela.\u003c/p\u003e\n\u003cp\u003eCom ela, o systemd adiciona uma ACL ao nó de dispositivo dando ao seu usuário logado permissões de leitura+escrita, automaticamente, por todo o tempo em que você estiver no teclado. Faça logout, a ACL desaparece. Pluge um segundo teclado Aero, a ACL aparece nele também. É o equivalente Linux de um cartão-chave de hotel: você ganha acesso enquanto é o hóspede, o sistema o retoma quando você sai.\u003c/p\u003e\n\u003cp\u003eGosto de pensar nas regras udev como uma \u003cstrong\u003ecarta de amor para o você-do-futuro\u003c/strong\u003e. Você as escreve uma vez, você as deixa em disco, e para sempre depois disso, a máquina dá as boas-vindas ao seu eu futuro — aquele que esqueceu tudo sobre este projeto e só quer que o teclado se acenda — como se nada nunca tivesse sido difícil. O script \u003ccode\u003einstall.sh\u003c/code\u003e faz este presente automaticamente na primeira instalação. Você nunca mais verá a carta de amor, porque você nunca mais precisará dela.\u003c/p\u003e\n\u003cp\u003eA regra udev resolveu a dança das permissões, mas resolvê-la também trouxe à tona uma pergunta mais silenciosa: agora que qualquer usuário logado podia controlar o dispositivo, \u003cem\u003ecomo\u003c/em\u003e eles deveriam de fato controlá-lo? Na linha de comando à 1h da manhã? A partir de um menu de bandeja enquanto se escreve um e-mail? Ambos, idealmente — e no momento em que disse \u0026ldquo;ambos\u0026rdquo; em voz alta, o próximo galho do projeto se desenhou sozinho.\u003c/p\u003e\n\u003ch2 id=\"dois-frontends-um-cérebro\" class=\"headerLink\"\u003e\n    \u003ca href=\"#dois-frontends-um-c%c3%a9rebro\" class=\"header-mark\" aria-label=\"Header mark for 'Dois Frontends, Um Cérebro'\"\u003e\u003c/a\u003e6 Dois Frontends, Um Cérebro\u003c/h2\u003e\u003cp\u003eUma vez que a camada de dispositivo funcionou, havia uma decisão a tomar: CLI ou GUI? Eu disse: por que não ambos. Mas eu também disse: \u003cstrong\u003enão duas vezes.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eO formato com que acabei é um clássico \u0026ldquo;dois frontends compartilhando um cérebro\u0026rdquo; — a mesma classe \u003ccode\u003eAeroKeyboardRGB\u003c/code\u003e no fundo, duas UIs muito diferentes em cima. Nenhuma das UIs sabe nada sobre HID. Nenhuma das UIs faz sua própria matemática de checksum. Ambas se apoiam na mesma classe de dispositivo, e o único estado que compartilham é um pequeno arquivo JSON no diretório de cache do usuário.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-10\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   ┌──────────────────────────┐    ┌─────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │   aeroctl-kbd  (CLI)     │    │   aeroctl-kbd-tray  (GTK)   │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │                          │    │                             │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │   argparse, stdin/stdout │    │   AppIndicator menu         │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   │   src/.../cli.py:69-162  │    │   src/.../tray.py:44-150    │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   └──────────────┬───────────┘    └──────────────┬──────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                  │                               │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                  │         both talk to          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                  ▼                               ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         ┌────────────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │           AeroKeyboardRGB (device.py)          │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │                                                │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    _find_hidraw  →  os.open  →  ioctl dance    │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │                                                │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    set_effect / get_effect / firmware / reset  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         └────────────────────────┬───────────────────────┘\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                  │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                  ▼\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                  /dev/hidraw?   ────────►   ITE-829X   ────►  RGB\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                           (keyboard LEDs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         ┌────────────────────────────────────────────────┐\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    ~/.cache/aeroctl-kbd-state.json             │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    { effect, speed, brightness, color, dir }   │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    written on \u0026#34;off\u0026#34;, read on \u0026#34;on\u0026#34;/\u0026#34;toggle\u0026#34;     │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         │    shared by CLI and tray                      │\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         └────────────────────────────────────────────────┘\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA CLI vive em \u003ccode\u003esrc/aeroctl_linux_indicator/cli.py:69-162\u003c/code\u003e. É uma porta de entrada \u003ccode\u003eargparse\u003c/code\u003e com subcomandos — \u003ccode\u003edevices\u003c/code\u003e, \u003ccode\u003estatus\u003c/code\u003e, \u003ccode\u003efirmware\u003c/code\u003e, \u003ccode\u003eset\u003c/code\u003e, \u003ccode\u003eoff\u003c/code\u003e, \u003ccode\u003eon\u003c/code\u003e, \u003ccode\u003etoggle\u003c/code\u003e, \u003ccode\u003ereset\u003c/code\u003e, \u003ccode\u003ekeyboard-mode\u003c/code\u003e, \u003ccode\u003eraw\u003c/code\u003e — e cada subcomando é um bloco \u003ccode\u003ewith kb:\u003c/code\u003e que abre o dispositivo, faz sua coisa e fecha.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-11\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/cli.py:95-118  (off/on, state round-trip)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;off\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ecur\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ekb\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003e_save_state\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;effect\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;effect\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;color\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;direction\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;direction\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ekb\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEFFECTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;static\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCOLORS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;black\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ok: off\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eargs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecmd\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;on\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003est\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003e_load_state\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ekb\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;effect\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003est\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;direction\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ok: on (restored)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ekb\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEFFECTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;static\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCOLORS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;white\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ok: on (default white)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO comando \u0026ldquo;off\u0026rdquo; não é \u0026ldquo;por favor escureça\u0026rdquo;. Por baixo dos panos, ele é: \u0026ldquo;capture o efeito atual em disco, depois defina o brilho como zero num efeito static-black\u0026rdquo;. O firmware do teclado não tem um desligamento real — ele tem \u003ccode\u003ebrightness = 0\u003c/code\u003e. O comando \u003ccode\u003eon\u003c/code\u003e é simétrico: leia o JSON, empurre o efeito anterior de volta. Se não houver estado prévio (primeiro boot, instalação limpa), ele recorre a um confortável padrão de branco a 100% de brilho.\u003c/p\u003e\n\u003cp\u003eO indicador de bandeja (\u003ccode\u003esrc/aeroctl_linux_indicator/tray.py:44-184\u003c/code\u003e) faz a mesma dança, mas dirigida por cliques de mouse. Ele constrói um menu GTK com três submenus — Brightness, Color, Effect — mais \u003ccode\u003eToggle On/Off\u003c/code\u003e, \u003ccode\u003eReset\u003c/code\u003e, \u003ccode\u003eStatus\u003c/code\u003e, \u003ccode\u003eQuit\u003c/code\u003e. Cada item de menu está conectado aos mesmos helpers:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-12\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/tray.py:62-83\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_set\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eeffect\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebrightness\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003espeed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edirection\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_safe\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003elambda\u003c/span\u003e \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEFFECTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eeffect\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003espeed\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebrightness\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eCOLORS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003edirection\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_set_brightness\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebrightness\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_op\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eAeroKeyboardRGB\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecur\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;effect\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebrightness\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;direction\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_safe\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_op\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_set_effect_only\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eeffect\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003e_op\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eAeroKeyboardRGB\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecur\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebri\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;brightness\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"mi\"\u003e100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eset_effect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eEFFECTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eeffect\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebri\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;color\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ecur\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;direction\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e_safe\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_op\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO wrapper \u003ccode\u003e_safe\u003c/code\u003e é uma confissão de três linhas: \u0026ldquo;Sou um app de bandeja, e se a abertura do dispositivo falhar eu não deveria derrubar o desktop inteiro\u0026rdquo;. Ele engole exceções e as registra no stderr. Isto não é elegante. É pragmático. Um app de bandeja que travou é uma experiência de usuário pior do que um app de bandeja que silenciosamente falha em acender seu teclado, e se algo der errado — cabo desplugado, regra udev faltando, alguém puxou a bateria — você ainda tem uma sessão GNOME funcionando da qual consertar.\u003c/p\u003e\n\u003cp\u003eComo ambos os frontends escrevem para o mesmo \u003ccode\u003e~/.cache/aeroctl-kbd-state.json\u003c/code\u003e, alternar a partir da bandeja e depois alternar a partir da CLI Faz A Coisa Certa. \u0026ldquo;Off pela bandeja, on pela CLI\u0026rdquo; restaura as mesmas cores e efeitos que você tinha. Essa pequena consistência é, honestamente, a razão inteira pela qual o indicador parece polido.\u003c/p\u003e\n\u003cp\u003ePolido no meu laptop, de qualquer forma. O pensamento que ficava me cutucando era: \u0026ldquo;polido no meu laptop\u0026rdquo; é uma armadilha — um indicador de bandeja que só roda em exatamente a distro, exatamente o Python e exatamente a build do AppIndicator que eu por acaso tenho instalada não é uma ferramenta, é um truque de festa. O que é como caí no buraco de coelho do PyInstaller.\u003c/p\u003e\n\u003ch2 id=\"pyinstaller--appindicator-um-ato-de-diplomacia-entre-distros\" class=\"headerLink\"\u003e\n    \u003ca href=\"#pyinstaller--appindicator-um-ato-de-diplomacia-entre-distros\" class=\"header-mark\" aria-label=\"Header mark for 'PyInstaller \u0026#43; AppIndicator: Um Ato de Diplomacia entre Distros'\"\u003e\u003c/a\u003e7 PyInstaller + AppIndicator: Um Ato de Diplomacia entre Distros\u003c/h2\u003e\u003cp\u003eOs desktops Linux são muitas coisas, mas \u0026ldquo;consistentes sobre APIs de bandeja de sistema\u0026rdquo; não é uma delas. A biblioteca original para isso se chamava \u003ccode\u003eAppIndicator3\u003c/code\u003e, mantida pela Canonical para o Unity. Quando o Unity morreu, foi adotada pelo MATE. Depois o GNOME a manteve viva por meio de uma extensão de shell. Então o Ubuntu 24.04 chegou — e com ele, o renome: \u003ccode\u003eAyatanaAppIndicator3\u003c/code\u003e, o fork-do-fork que todos deveriam usar agora.\u003c/p\u003e\n\u003cp\u003eInstalações novas no 24.04 trazem apenas o Ayatana. Sistemas mais antigos e algumas distros trazem apenas o legado. Algumas poucas trazem ambos. Eu precisava que meu binário funcionasse nos três.\u003c/p\u003e\n\u003cp\u003eA bandeja lida com isso em tempo de import com um pequeno try/except que tenta o nome moderno primeiro e recorre ao outro:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-13\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# src/aeroctl_linux_indicator/tray.py:12-17\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003egi\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erequire_version\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;AyatanaAppIndicator3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0.1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAppIndicator\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eimportlib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimport_module\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;gi.repository.AyatanaAppIndicator3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ne\"\u003eValueError\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ne\"\u003eImportError\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003egi\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erequire_version\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;AppIndicator3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0.1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eAppIndicator\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eimportlib\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimport_module\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;gi.repository.AppIndicator3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eTudo bem para um import de Python. Não tudo bem para o PyInstaller, que tem de saber em tempo de build quais módulos empacotar. Então adicionei um teste-de-cheiro correspondente ao script de build: ele roda um pequeno snippet inline de Python contra o interpretador ativo, verifica qual das duas bibliotecas está de fato instalada e imprime o caminho de import. O shell então passa esse caminho para o PyInstaller como \u003ccode\u003e--hidden-import\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ebash\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-14\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# scripts/build-binaries.sh:14-24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eAPPINDICATOR_IMPORT\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eAPPINDICATOR_IMPORT\u003c/span\u003e\u003cspan class=\"k\"\u003e:-$(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$PYTHON_BIN\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e - \u003cspan class=\"s\"\u003e\u0026lt;\u0026lt;\u0026#39;PY\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eimport gi\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003etry:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    gi.require_version(\u0026#34;AyatanaAppIndicator3\u0026#34;, \u0026#34;0.1\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    print(\u0026#34;gi.repository.AyatanaAppIndicator3\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003eexcept ValueError:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    gi.require_version(\u0026#34;AppIndicator3\u0026#34;, \u0026#34;0.1\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e    print(\u0026#34;gi.repository.AppIndicator3\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003ePY\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEste é o tipo de detalhe que não entra em títulos de post de blog, porque \u0026ldquo;adicionei um heredoc de 18 linhas ao meu script de build\u0026rdquo; não é uma manchete. Mas é genuinamente a diferença entre \u0026ldquo;roda na minha máquina\u0026rdquo; e \u0026ldquo;roda na máquina do usuário também\u0026rdquo;. Quando alguém clona o repo no Fedora, o teste-de-cheiro resolve para o Ayatana. Quando alguém o clona numa caixa 20.04 LTS que nunca recebeu o recado, ele resolve para o nome legado. O PyInstaller empacota o correto e o binário Simplesmente Funciona.\u003c/p\u003e\n\u003cp\u003eO binário da CLI é construído com \u003ccode\u003e--onefile\u003c/code\u003e. O binário da bandeja é construído com \u003ccode\u003e--onedir\u003c/code\u003e, porque o runtime do GTK precisa de muitos arquivos irmãos (caches de ícone, arquivos de schema, typelibs) que o PyInstaller não consegue inlinar num único executável sem sofrimento. O script \u003ccode\u003einstall.sh\u003c/code\u003e então faz tar da árvore onedir, a extrai em \u003ccode\u003e~/.local/bin/aeroctl-kbd-tray/\u003c/code\u003e e cria uma entrada \u003ccode\u003eautostart\u003c/code\u003e desktop que aponta para o binário da bandeja lá dentro.\u003c/p\u003e\n\u003cp\u003eNeste ponto, a história do software estava efetivamente terminada — a parte de entregar, de qualquer forma. O que restava era o lado da história do hardware, que eu só pude ler em retrospecto, pelo log de commits. E um desses commits é a razão pela qual estou escrevendo este ensaio.\u003c/p\u003e\n\u003ch2 id=\"limites-de-hardware-contados-pelo-log-de-commits\" class=\"headerLink\"\u003e\n    \u003ca href=\"#limites-de-hardware-contados-pelo-log-de-commits\" class=\"header-mark\" aria-label=\"Header mark for 'Limites de Hardware, Contados pelo Log de Commits'\"\u003e\u003c/a\u003e8 Limites de Hardware, Contados pelo Log de Commits\u003c/h2\u003e\u003cp\u003eSe você ler o git log do projeto de cima a baixo, ele se lê quase como um conto. Vou citar hashes de commit — os reais — porque é aqui que a narrativa ficou de verdade.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-15\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e550ee28  feat: setup project, cli, tray and configure auto-releases      (1077 LoC)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e00055f2  test: add test suite, mock hardware IO and integrate pytest     (coverage to CI)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e74646e6  fix: tray mock attribute error and boost test coverage to ~78%\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecaea8df  test: add missing branches coverage to cli.py reaching +80%\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ef39a4b1  fix(install): use python environment for pyinstaller build\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ef4a204e  fix(install): auto-start the tray indicator immediately\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e68dbdae  chore: remove unsupported RGB effects                           ← the sad one\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ee1ac392  docs: add supported device model AERO X16 2WH\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e48c0f46  docs: clarify native hardware RGB effect limitations\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003e550ee28\u003c/code\u003e é o despejo inicial: 1.077 linhas de uma vez, 16 arquivos, a CLI, a bandeja, o script de build, o script de instalação, o README. É sempre assim que o primeiro commit de um projeto de engenharia reversa se parece — não porque você não saiba sobre boa higiene de commits, mas porque você acabou de passar um mês com ele vivendo num único diretório sem versionamento enquanto descobria se a coisa funcionaria de algum modo.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e00055f2\u003c/code\u003e é a primeira suíte de testes. Mockei a camada de hardware (\u003ccode\u003e/dev/hidraw\u003c/code\u003e não existe na CI), de modo que o test harness pudesse exercitar a construção de pacotes, o round-trip de estado e o parsing de argumentos da CLI sem precisar de um teclado físico no runner.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e74646e6\u003c/code\u003e é o vexame. Eu havia mockado \u003ccode\u003eAppIndicator.Indicator.new\u003c/code\u003e, mas esquecido um atributo que ele retorna, e os testes da bandeja falharam com um \u003ccode\u003eAttributeError\u003c/code\u003e lá no fundo do encanamento do GTK. Uma linha, um commit, 35 novas linhas de teste e um aumento de cobertura para ~78%.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ecaea8df\u003c/code\u003e me empurrou para além de 80% de cobertura lógica em \u003ccode\u003ecli.py\u003c/code\u003e — adicionou 56 linhas em \u003ccode\u003etests/test_cli.py\u003c/code\u003e atingindo cada galho de subcomando. Para um projeto de 593 linhas, 80% é o ponto doce onde cada caminho principal é exercitado mas eu não estou escrevendo testes para internos do \u003ccode\u003eargparse\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ef39a4b1\u003c/code\u003e e \u003ccode\u003ef4a204e\u003c/code\u003e são as dores de crescimento do script de instalação. \u003ccode\u003ef39a4b1\u003c/code\u003e fez duas coisas: traduziu o script inteiro do espanhol (uma das línguas nativas do autor) para o inglês para que não-falantes de espanhol pudessem auditá-lo, e fez a build do PyInstaller usar um virtualenv local para que o script não tropeçasse na PEP-668 em distros modernas baseadas em Debian. \u003ccode\u003ef4a204e\u003c/code\u003e é o pequeno polimento que diz \u0026ldquo;depois de uma instalação nova, também lance a bandeja agora mesmo, em vez de fazer o usuário fazer logout e login de novo\u0026rdquo;. Essa é a chamada \u003ccode\u003enohup\u003c/code\u003e no fim do \u003ccode\u003einstall.sh\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eE então há \u003ccode\u003e68dbdae\u003c/code\u003e: \u003cstrong\u003e\u0026ldquo;chore: remove unsupported RGB effects\u0026rdquo;.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eSão seis linhas removidas de \u003ccode\u003edevice.py\u003c/code\u003e e oito linhas alteradas em \u003ccode\u003etray.py\u003c/code\u003e. É também o momento em que aprendi que meu novinho Aero X16 era menos capaz do que um Aero 15 de 2019. Olhe o diff:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003epython\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-16\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# commit 68dbdae, src/aeroctl_linux_indicator/device.py, removed:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;ripple\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;neon\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e8\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;rainbow\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;circle\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e11\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;hedge\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;rotate\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e13\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eEsses são efeitos reais. Eles existem no protocolo de firmware do Windows. Eles estão documentados. Eles simplesmente não funcionam no Aero 16 de 12ª geração. O firmware aceita o comando, mas o teclado não renderiza o efeito. Modelos mais novos descarregam esses efeitos para o \u003cstrong\u003eGigabyte Control Center\u003c/strong\u003e — um serviço exclusivo do Windows que pinta as teclas quadro a quadro por USB a partir do userspace, porque o firmware não sabe mais como fazê-los por si só.\u003c/p\u003e\n\u003cdiv class=\"details admonition note open\"\u003e\n    \u003cdiv class=\"details-summary admonition-title\"\u003e\n        \u003cspan class=\"icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z\"/\u003e\u003c/svg\u003e\u003c/span\u003ePor que `68dbdae` é a linha mais triste do repositório\u003cspan class=\"details-icon\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"details-content\"\u003e\n        \u003cdiv class=\"admonition-content\"\u003eNão é uma correção de bug. Não é um refactor. É um reconhecimento de que a Gigabyte tomou uma decisão de negócio — \u0026ldquo;deixe o app do Windows fazer a renderização, enxugue o firmware\u0026rdquo; — e essa decisão é permanentemente visível no meu teclado Linux na forma de seis nomes de efeito que não existem mais.\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003ee1ac392\u003c/code\u003e e \u003ccode\u003e48c0f46\u003c/code\u003e são os commits de README que adicionaram uma seção dedicada explicando isso aos usuários, para que ninguém mais tenha de aprender da forma que eu aprendi.\u003c/p\u003e\n\u003cp\u003eO que me resta são os sete efeitos que o firmware ainda fala nativamente: \u003ccode\u003estatic\u003c/code\u003e, \u003ccode\u003ebreathing\u003c/code\u003e, \u003ccode\u003ewave\u003c/code\u003e, \u003ccode\u003efade\u003c/code\u003e, \u003ccode\u003emarquee\u003c/code\u003e, \u003ccode\u003eflash\u003c/code\u003e, \u003ccode\u003eraindrop\u003c/code\u003e. Eles bastam. Eles são bonitos. Eles eram, antes de \u003ccode\u003e68dbdae\u003c/code\u003e, acompanhados por \u003ccode\u003eripple\u003c/code\u003e e \u003ccode\u003eneon\u003c/code\u003e e \u003ccode\u003erainbow\u003c/code\u003e, e sinto falta deles.\u003c/p\u003e\n\u003ch2 id=\"lições\" class=\"headerLink\"\u003e\n    \u003ca href=\"#li%c3%a7%c3%b5es\" class=\"header-mark\" aria-label=\"Header mark for 'Lições'\"\u003e\u003c/a\u003e9 Lições\u003c/h2\u003e\u003cp\u003eSe eu fosse fazer isto de novo, carregaria adiante um punhado de lições. Não muitas. Estes pequenos projetos ensinam suas lições em pequenas mordidas.\u003c/p\u003e\n\u003ch3 id=\"a-detecção-é-frágil-entre-gerações\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-detec%c3%a7%c3%a3o-%c3%a9-fr%c3%a1gil-entre-gera%c3%a7%c3%b5es\" class=\"header-mark\" aria-label=\"Header mark for 'A detecção é frágil entre gerações'\"\u003e\u003c/a\u003e9.1 A detecção é frágil entre gerações\u003c/h3\u003e\u003cp\u003eCada geração de Gigabyte Aero tem suas próprias peculiaridades. O vendor ID permanece \u003ccode\u003e0x0414\u003c/code\u003e, mas o product ID deriva, o report descriptor deriva, a tabela de efeitos deriva. Meu scanner usa a usage page \u003ccode\u003e0xFF01\u003c/code\u003e como sua âncora precisamente porque esse é o sinal mais estável — se um modelo futuro mudar os product IDs, contanto que o fabricante continue usando a família de controlador ITE, meu glob de sysfs e o sniff de descriptor provavelmente ainda o encontrarão. A alternativa (cravar PIDs no código) teria apodrecido em menos de um ano.\u003c/p\u003e\n\u003ch3 id=\"o-fallback-ayatanalegado-é-o-polimento-que-importa\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-fallback-ayatanalegado-%c3%a9-o-polimento-que-importa\" class=\"header-mark\" aria-label=\"Header mark for 'O fallback Ayatana/legado é o polimento que importa'\"\u003e\u003c/a\u003e9.2 O fallback Ayatana/legado é o polimento que importa\u003c/h3\u003e\u003cp\u003eAs 18 linhas de heredoc em \u003ccode\u003ebuild-binaries.sh:14-24\u003c/code\u003e não são glamorosas. Ninguém jamais escreverá um post de blog sobre o \u003ccode\u003etry/except ValueError\u003c/code\u003e em \u003ccode\u003etray.py:12-17\u003c/code\u003e. Mas juntos eles são a diferença entre \u0026ldquo;usuários Linux com a distro certa\u0026rdquo; e \u0026ldquo;usuários Linux\u0026rdquo;. Se você vai entregar um utilitário de desktop em 2026, você faz a dança do Ayatana. É o oposto de overengineering: são dez minutos de trabalho que silenciosamente cobrem três anos de fragmentação de distros.\u003c/p\u003e\n\u003ch3 id=\"uma-base-de-593-linhas-pode-atingir-80-de-cobertura--se-a-camada-de-hardware-for-mockada\" class=\"headerLink\"\u003e\n    \u003ca href=\"#uma-base-de-593-linhas-pode-atingir-80-de-cobertura--se-a-camada-de-hardware-for-mockada\" class=\"header-mark\" aria-label=\"Header mark for 'Uma base de 593 linhas pode atingir 80% de cobertura — se a camada de hardware for mockada'\"\u003e\u003c/a\u003e9.3 Uma base de 593 linhas pode atingir 80% de cobertura — se a camada de hardware for mockada\u003c/h3\u003e\u003cp\u003eA maior vitória isolada para os testes foi me comprometer com um \u003ccode\u003eAeroKeyboardRGB\u003c/code\u003e falso em \u003ccode\u003etests/conftest.py\u003c/code\u003e. Uma vez que o hardware ficou mockável, cada subcomando da CLI virou uma função pura de suas entradas: argparse para dentro, interações de dispositivo falsas para fora. A CI roda sem um teclado plugado no runner. Os testes rodam numa fração de segundo. E os commits que elevaram a cobertura (\u003ccode\u003e74646e6\u003c/code\u003e, \u003ccode\u003ecaea8df\u003c/code\u003e) adicionaram menos de 100 linhas de teste entre eles. Base pequena, suíte de testes pequena, cobertura real. É um bom lugar para se estar.\u003c/p\u003e\n\u003ch3 id=\"confie-no-checksum-não-na-sua-intuição\" class=\"headerLink\"\u003e\n    \u003ca href=\"#confie-no-checksum-n%c3%a3o-na-sua-intui%c3%a7%c3%a3o\" class=\"header-mark\" aria-label=\"Header mark for 'Confie no checksum, não na sua intuição'\"\u003e\u003c/a\u003e9.4 Confie no checksum, não na sua intuição\u003c/h3\u003e\u003cp\u003eEsta é a que eu tatuaria em mim mesmo se pudesse. Quando você está fazendo engenharia reversa de um protocolo binário e o algoritmo óbvio não corresponde à captura, não seja esperto. Não assuma que o fabricante está usando uma variante conhecida de CRC. Tente as coisas burras primeiro, depois tente variantes ligeiramente-erradas das coisas burras, depois sente com a matemática até ela encaixar. O checksum do teclado Gigabyte é \u0026ldquo;faça o pacote totalizar \u003ccode\u003e0xFF\u003c/code\u003e\u0026rdquo;, o que ninguém chamaria de estado-da-arte, mas é o que o firmware de fato computa, e em engenharia reversa, \u0026ldquo;o que o firmware de fato computa\u0026rdquo; vence \u0026ldquo;o que você acha que ele deveria computar\u0026rdquo; toda vez.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eMeu teclado brilha, agora. Ele brilha quando faço login, porque o \u003ccode\u003einstall.sh\u003c/code\u003e largou um arquivo de autostart em \u003ccode\u003e~/.config/autostart/\u003c/code\u003e. Ele lembra sua última cor depois que eu o ligo e desligo, porque \u003ccode\u003ecli.py\u003c/code\u003e e \u003ccode\u003etray.py\u003c/code\u003e ambos escrevem para o mesmo pequeno arquivo JSON em \u003ccode\u003e~/.cache/\u003c/code\u003e. Ele funciona sem \u003ccode\u003esudo\u003c/code\u003e, por causa de uma cláusula \u003ccode\u003eTAG+=\u0026quot;uaccess\u0026quot;\u003c/code\u003e numa regra udev que configurei uma vez e nunca mais pensarei a respeito.\u003c/p\u003e\n\u003cp\u003eE em algum lugar debaixo de tudo isso, um pacote de 9 bytes — oito bytes de intenção, um byte de checksum não-XOR — está sendo entregue do Python ao \u003ccode\u003efcntl.ioctl\u003c/code\u003e ao driver hidraw do Linux a um controlador ITE-829X que finalmente, pela primeira vez desde que apaguei o Windows, entende o que eu quero.\u003c/p\u003e\n\u003cp\u003eO que era, o tempo todo, apenas o teclado dizer algo de volta.\u003c/p\u003e\n",
        "language": "pt"
    },
    {
        "title" : "As Crônicas do Gocracker: uma microVM em Go, de hack de fim de semana a sandbox de produção",
        "date_published" : "2026-05-19T00:00:00Z",
        "date_modified" : "2026-05-19T00:00:00Z",
        "id" : "https://misael.org/pt/gocracker-part-1-foundation/",
        "url" : "https://misael.org/pt/gocracker-part-1-foundation/",
        "summary": "Toda a história do gocracker de uma só vez: por que Go, como o KVM realmente funciona, os bugs de concorrência que quase o mataram, o trabalho de performance que trouxe o cold boot para menos de 200ms, e a camada de produção acima dele que quase se matou silenciosamente.",
        "content_html" : "\u003ch2 id=\"o-firecracker-é-ótimo-mesmo-assim-escrevi-outro\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-firecracker-%c3%a9-%c3%b3timo-mesmo-assim-escrevi-outro\" class=\"header-mark\" aria-label=\"Header mark for 'O Firecracker é ótimo. Mesmo assim, escrevi outro.'\"\u003e\u003c/a\u003e1 O Firecracker é ótimo. Mesmo assim, escrevi outro.\u003c/h2\u003e\u003cp\u003eEu amo o Firecracker. Eu colocaria um adesivinho do Firecracker no meu laptop se a AWS distribuísse um. A ideia — bootar uma VM Linux de verdade em milissegundos, descascar todo dispositivo que ninguém precisa, trancar os syscalls, e dar o dia por encerrado — é uma das ideias de engenharia de sistemas mais elegantes da última década. Ela transformou \u0026ldquo;container, mas de verdade isolado\u0026rdquo; de meme em produto.\u003c/p\u003e\n\u003cp\u003eEntão, naturalmente, num fim de semana, decidi substituí-lo.\u003c/p\u003e\n\u003cp\u003eNão porque ele seja ruim. Mas porque toda vez que eu queria rodar \u003ccode\u003eubuntu:22.04\u003c/code\u003e como uma microVM, havia seis passos manuais entre mim e um prompt: baixar a imagem, extrair o rootfs, construir um disco ext4, gerar um initrd, escrever uma config JSON de quarenta linhas, criar um dispositivo TAP, brigar com o iptables, \u003cem\u003eaí então\u003c/em\u003e chamar a API. O Firecracker é um especialista em Rust. Ele boota VMs lindamente e assume que o resto é problema seu. Esse é o design certo para a AWS, onde cada passo é mais um serviço especialista. Para uma pessoa rodando coisas num laptop, é uma catástrofe de abas.\u003c/p\u003e\n\u003cp\u003eEu queria um generalista. \u003ccode\u003egocracker run --image ubuntu:22.04\u003c/code\u003e, e queria que ele já tivesse feito tudo isso na hora em que eu terminasse de apertar Enter. Então escrevi um. Em Go. Porque a linguagem onde \u0026ldquo;baterias inclusas\u0026rdquo; é a cultura é o lugar óbvio para construir uma microVM com baterias inclusas.\u003c/p\u003e\n\u003cp\u003eSe o Firecracker é CGI — uma interface crua e principista que qualquer componente esperto pode dirigir — então o gocracker é FastCGI: a mesma interface com um processo confortável e de longa duração envolto nela, que assume que você é um desenvolvedor num laptop e gostaria de tocar o seu dia.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-1\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    FIRECRACKER                gocracker\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    (Rust specialist)          (Go generalist)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    just a VMM                 VMM + OCI + initrd + TAP + Compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    you bring:                 you bring:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - rootfs                   - one command\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - kernel                   - one kernel\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - initrd\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - tap + NAT\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - JSON config\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      - 40 lines of bash\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    beautiful.                 I am lazy and I like it.\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003ch2 id=\"o-que-o-kvm-realmente-é\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-que-o-kvm-realmente-%c3%a9\" class=\"header-mark\" aria-label=\"Header mark for 'O que o KVM realmente é'\"\u003e\u003c/a\u003e2 O que o KVM realmente é\u003c/h2\u003e\u003cp\u003eO KVM — a Kernel-based Virtual Machine — é um dispositivo de caractere, \u003ccode\u003e/dev/kvm\u003c/code\u003e, que expõe a virtualização de hardware como ioctls. É só isso. Abra um arquivo, chame ioctls nele, e uma CPU começa a rodar código em seu nome dentro de uma sandbox de hardware.\u003c/p\u003e\n\u003cp\u003eGosto de descrever os ioctls do KVM como a linguagem assembly das VMs. Baixo nível, ortogonais, cada um faz exatamente uma coisa, e você mesmo tem que compô-los em algo útil. Não há scheduler, não há modelo de dispositivos. Há um pequeno alfabeto de primitivas — criar a VM, criar a vCPU, mapear memória, rodar, obter registradores, definir registradores — e é você quem transforma isso num computador.\u003c/p\u003e\n\u003cp\u003eO coração de todo VMM já escrito é um loop. Você chama run na vCPU. A thread do host bloqueia. O guest roda. Eventualmente o guest faz algo que precisa da atenção do host — toca um registrador MMIO, atinge uma porta de I/O, recebe uma interrupção, desliga — e o KVM devolve o controle. Esse retorno se chama VMexit. Todo o resto — dispositivos, boot loaders, engines de snapshot — é açúcar em volta desse loop.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-2\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            HOST                              GUEST\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    +---------------------+            +-------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    | ioctl(KVM_RUN)      | ---------\u0026gt; | guest instructions|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                     |            |                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                     | \u0026lt;--------- | VMexit            |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    | dispatch(exit_reason)            | (MMIO / IO / IRQ) |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   handle_mmio()     |            |                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   handle_io()       |            |                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   inject_irq()      | ---------\u0026gt; | resume            |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    +---------------------+            +-------------------+\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eTodo o binding com o KVM mora num único arquivo, e o número central tem exatamente cinco dígitos hexadecimais:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-3\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// internal/kvm/kvm.go\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003ekvmRun\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0xAE80\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eQuando o userspace chama \u003ccode\u003eioctl(vcpu_fd, 0xAE80, 0)\u003c/code\u003e, o kernel transfere o controle para a CPU do guest. A thread do host bloqueia. O guest roda. Esse é o coração de todo VMM já escrito.\u003c/p\u003e\n\u003ch2 id=\"uma-goroutine-por-vcpu-é-o-modelo-inteiro\" class=\"headerLink\"\u003e\n    \u003ca href=\"#uma-goroutine-por-vcpu-%c3%a9-o-modelo-inteiro\" class=\"header-mark\" aria-label=\"Header mark for 'Uma goroutine por vCPU é o modelo inteiro'\"\u003e\u003c/a\u003e3 Uma goroutine por vCPU é o modelo inteiro\u003c/h2\u003e\u003cp\u003eAqui está a alegria de escrever um VMM em Go, e a coisa que fez o fim de semana parecer um fim de semana. Uma CPU virtual é um file descriptor que bloqueia até o guest sair. Se você quer duas vCPUs, você quer duas threads de host. Se quer dezesseis, quer dezesseis threads.\u003c/p\u003e\n\u003cp\u003eGo tem uma palavra para \u0026ldquo;uma coisa que parece uma thread que bloqueia num syscall.\u0026rdquo; A nota de rodapé irritante é que o scheduler do Go normalmente move goroutines entre threads do SO sempre que tem vontade, e o KVM \u003cem\u003erealmente\u003c/em\u003e não gosta disso. A correção tem três palavras:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-4\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eruntime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eLockOSThread\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eÉ isso. É essa a história do multiprocessamento. Cada vCPU ganha uma goroutine. Cada goroutine trava sua thread e roda o loop de saída. O runtime cuida do resto. Nenhum thread pool para escrever. Nem mesmo uma primitiva de sincronização — os channels na sua caixa de ferramentas já funcionam.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-5\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            per-vCPU goroutine (LockOSThread)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    +--------------------------------------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |                                                  |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   for {                                          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       err := ioctl(vcpu_fd, KVM_RUN, 0)          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       if err == EINTR || err == EAGAIN {         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |           continue          // transient; resume |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       }                                          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       switch run.exit_reason {                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       case IO:        handleIO(run.io)           |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       case MMIO:      handleMMIO(run.mmio)       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       case SHUTDOWN:  return                     |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       case INTR:      continue                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       case HLT:       waitForIRQ(); continue     |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |       }                                          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    |   }                                              |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    +--------------------------------------------------+\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eQuando um guest de verdade boota, esse loop roda milhões de vezes por segundo. A maioria das saídas é rápida — um notify de fila virtio, uma escrita na UART, um tick de timer — e o loop retorna para dentro do \u003ccode\u003eKVM_RUN\u003c/code\u003e antes que alguém perceba. A arte é tornar cada caso do switch barato.\u003c/p\u003e\n\u003cp\u003eNo fim do fim de semana, eu tinha um prompt verde, uma VM rodando, e um cold boot abaixo de 400 ms. Eu colocaria Rust no meu adesivo de laptop também. Mas o Go me deu goroutines que fizeram de \u0026ldquo;uma thread por vCPU\u0026rdquo; uma feature de três palavras, um servidor de API HTTP em talvez duzentas linhas, config JSON que simplesmente funciona, e o ecossistema de bibliotecas OCI mais maduro de qualquer linguagem. Compilar para ARM64 leva duas variáveis de ambiente. As brigas que tive com Go foram reais, mas limitadas; a conveniência se acumulou ao longo de meses.\u003c/p\u003e\n\u003ch2 id=\"as-três-semanas-seguintes\" class=\"headerLink\"\u003e\n    \u003ca href=\"#as-tr%c3%aas-semanas-seguintes\" class=\"header-mark\" aria-label=\"Header mark for 'As três semanas seguintes'\"\u003e\u003c/a\u003e4 As três semanas seguintes\u003c/h2\u003e\u003cp\u003eA VM bootou. Um comando. A vida estava linda. E então a mesma propriedade de goroutines-baratas que tornou o primeiro fim de semana prazeroso começou a cobrar juros.\u003c/p\u003e\n\u003cp\u003eCada bug começava como um sintoma diferente e batia num beco sem saída no mesmo lugar: duas goroutines discordando sobre quem era dono de um pedaço de estado do kernel. O jailer ficava deixando os sapatos na porta — bind mounts sobrevivendo a uma sandbox que crashou, envenenando a próxima VM. Um \u003ccode\u003eclose\u003c/code\u003e cru num file descriptor vsock compartilhado acabou sendo uma operação de refcount, não uma mensagem de protocolo; o host bloqueava para sempre até o usuário apertar uma tecla de puro desespero, o que desbloqueava uma goroutine de fundo, que soltava a última referência, que finalmente enviava o pacote de shutdown. Um panic durante a limpeza deixava o terminal em modo raw. Cada release do runtime do Go queria mais um syscall que o filtro seccomp não conhecia, e \u0026ldquo;Bad system call\u0026rdquo; virou a trilha sonora da semana de release.\u003c/p\u003e\n\u003cp\u003eQuinze goroutines num sobretudo continuam sendo quinze goroutines. A lição foi menos sobre qualquer bug específico e mais sobre o formato de todos eles: numa microVM, o kernel do host mantém estado sobre a sua VM, e se você não limpá-lo no caminho feliz, ninguém vai limpá-lo no caminho triste. Depois da quinta corrida em três dias, a correção estrutural não foi \u0026ldquo;seja mais cuidadoso\u0026rdquo; — eu já estava sendo cuidadoso — mas sim ligar \u003ccode\u003e-race\u003c/code\u003e no CI e escrever testes que deliberadamente corriam produtor e consumidor para fazer as condições de corrida aparecerem de forma reproduzível. O detector de corrida é a configuração mais importante que você pode ligar num projeto Go. Ele é de dez a cem vezes mais lento, e vale cada ciclo.\u003c/p\u003e\n\u003cp\u003eQuando o CI parou de oscilar, eu finalmente tinha uma VM em que confiava o suficiente para medir.\u003c/p\u003e\n\u003ch2 id=\"eu-estava-olhando-para-a-caixa-errada\" class=\"headerLink\"\u003e\n    \u003ca href=\"#eu-estava-olhando-para-a-caixa-errada\" class=\"header-mark\" aria-label=\"Header mark for 'Eu estava olhando para a caixa errada'\"\u003e\u003c/a\u003e5 Eu estava olhando para a caixa errada\u003c/h2\u003e\u003cp\u003ePor cerca de duas semanas, achei que o gocracker tinha um problema de 2× contra o Firecracker. O campo \u003ccode\u003eduration\u003c/code\u003e que eu estava imprimindo ia de ~30 ms sem o jailer para ~55 ms com ele. Duas vezes pior. O fork-exec era o vilão. O jailer era o vilão. Sete PUTs REST eram o vilão.\u003c/p\u003e\n\u003cp\u003eOlhe para o \u003cem\u003erelógio de parede\u003c/em\u003e, porém. Cerca de 860 ms vs cerca de 880 ms. Cerca de vinte milissegundos de diferença, sobre uma baseline de 860 ms. Isso não é 2×; é cerca de 2%, e 2% é ruído. O \u0026ldquo;2×\u0026rdquo; estava inteiramente num campo \u003ccode\u003eduration\u003c/code\u003e que media coisas diferentes nos dois caminhos de código. O caminho in-process media o \u003ccode\u003evmm.New\u003c/code\u003e cru. O caminho do worker media o fork-exec, o setup do jailer, o chroot, sete PUTs REST, \u003cem\u003emais\u003c/em\u003e o \u003ccode\u003evmm.New\u003c/code\u003e. Ambos paravam o relógio antes do kernel do guest ter impresso um único byte. Nenhum dos números media o tempo até um guest útil. Nenhum era comparável ao outro.\u003c/p\u003e\n\u003cp\u003eDividir a medição em quatro fases honestas — orquestração, setup do VMM, start, primeira saída do guest — fez o 2× evaporar:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-6\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// pkg/vmm/timings.go\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// BootTimings is the per-phase breakdown of how long it took to\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// bring a microVM to life.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//   - Orchestration:    host-side work *before* the guest kernel starts\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//   - VMMSetup:         time inside vmm.New() — KVM_CREATE_VM, memory...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//   - Start:            KVM_RUN starts on the vCPU goroutines\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//   - GuestFirstOutput: first byte the guest prints on the UART\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO jailer custou aproximadamente 30 ms na orquestração, em cima de um boot de kernel do guest de ~300 ms que ambos os caminhos compartilhavam. Um imposto honesto de ~10% na orquestração, não uma penalidade de 2×.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-7\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   before the breakdown                   after the breakdown\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   (one misleading number)            (four honest numbers)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   +-----------------------+         +--------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   | duration = ~55ms      |         | orchestration ~30ms|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   | (in runViaWorker this |         | vmm_setup      ~8ms|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   |  includes jailer,     |         | start          ~2ms|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   |  fork, a few REST     |         | guest_first  ~320ms|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   |  PUTs, then vmm.New,  |         | total        ~360ms|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   |  then start)          |         +--------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   +-----------------------+            |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                        v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u0026#34;2x slower\u0026#34;                 \u0026#34;I was staring\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                     at the wrong box.\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eE então outra coisa caiu por terra: cerca de trezentos daqueles milissegundos eram o \u003cem\u003eLinux bootando dentro da VM\u003c/em\u003e. Se eu quisesse uma VM mais rápida, o meu código não era o problema. O meu kernel era.\u003c/p\u003e\n\u003cp\u003eDepurar performance é auditar contas de despesa. Você encara as linhas de item. Continua encarando até achar a que diz \u0026ldquo;almoço de negócios $480\u0026rdquo; e o restaurante acaba sendo um Costco. A pegadinha nunca está onde você espera.\u003c/p\u003e\n\u003ch2 id=\"o-kernel-era-o-problema\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-kernel-era-o-problema\" class=\"header-mark\" aria-label=\"Header mark for 'O kernel era o problema'\"\u003e\u003c/a\u003e6 O kernel era o problema\u003c/h2\u003e\u003cp\u003eEu bifurquei o kernel do guest em dois perfis: um genérico, que envio por padrão, e um \u0026ldquo;mínimo\u0026rdquo; que arranca qualquer coisa que uma VM com virtio e nada mais jamais precisará. ACPI NUMA foi. Hibernação foi. O subsistema USB inteiro foi. Gerenciamento de energia, profiling, SCSI, dispositivos loop, XFS, NFS — tudo embora. Virtio ficou. ext4 ficou. kvm-clock ficou. O kernel encolheu cerca de 12% e o boot caiu um bom pedaço só por rodar menos initcalls.\u003c/p\u003e\n\u003cp\u003eEntão veio a pequena mudança que importou mais do que qualquer outra. Adicionei um parâmetro à linha de comando do kernel: \u003ccode\u003eloglevel=4\u003c/code\u003e. Ele diz ao kernel \u0026ldquo;só imprima avisos e acima no console; todo o resto ainda vai para o ring buffer, então você pode ver via dmesg.\u0026rdquo; A maior parte da saída do boot parou de ir para a UART emulada.\u003c/p\u003e\n\u003cp\u003eAcontece que uma UART virtualizada é cara por byte. Cada byte que o kernel escreve no console serial é uma saída MMIO para o userspace, que é uma troca de contexto, que são alguns microssegundos de tempo desperdiçado. Multiplique por alguns milhares de bytes em tempo de boot, e o boot era dominado por \u003cem\u003eimpressão\u003c/em\u003e. Silenciar o console tirou cerca de 130 ms do boot.\u003c/p\u003e\n\u003cp\u003eUma linha.\u003c/p\u003e\n\u003cp\u003eAs vitórias menores seguiram o mesmo tema: pare de brigar com o kernel, e peça que ele faça o seu trabalho. Cachear uma sonda de discard cuja resposta só depende do filesystem do host, não do guest. Rotear interrupções x86 através de \u003ccode\u003eeventfd + IRQFD\u003c/code\u003e em vez de um ioctl por asserção, do mesmo jeito que o backend ARM64 já fazia. Desligar o garbage collector do Go no subprocesso VMM de vida curta:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-8\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// cmd/gocracker-vmm/main.go\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;runtime/debug\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003einit\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003edebug\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eSetGCPercent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// short-lived process; let the OS reap memory\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO processo não precisa de um GC; ele alegremente roda até o fim, e o SO recolhe a sua memória. Alguns milissegundos aqui, alguns milissegundos ali. Não é genial, individualmente. É cumulativo.\u003c/p\u003e\n\u003cp\u003eEmpilhando as vitórias como um gráfico de barras na escala aproximada das medições reais:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-9\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e standard kernel:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   [==orch==][vmm][======guest_first_output: ~305ms======]   ~390ms\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    ~70ms    ~15  ~305\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                              this is Linux booting.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e minimal kernel:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   [==orch==][vmm][=====guest_first_output: ~280ms=====]     ~365ms (-25)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                              fewer init calls.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e minimal + loglevel=4:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   [==orch==][vmm][guest_first_output: ~170ms]                ~250ms (-115)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                              80% of the cost\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                              was *printing*.\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eDepois de tudo isso: cold boot na faixa de 150–170 ms. Cerca de 45 ms atrás do Firecracker, vindo de muito mais. Um gap Go-vs-Rust mensurável em milissegundos, sobre um boot dominado por um kernel Linux estrangeiro que não controlo. Esse é o lugar certo onde acabar. Se alguém te disser que a microVM dele está algumas dezenas de milissegundos atrás do Firecracker, você acena educadamente; se disser que está 2× atrás, você tem perguntas.\u003c/p\u003e\n\u003ch2 id=\"quando-o-cold-boot-ficou-pequeno-o-caminho-quente-ficou-constrangedor\" class=\"headerLink\"\u003e\n    \u003ca href=\"#quando-o-cold-boot-ficou-pequeno-o-caminho-quente-ficou-constrangedor\" class=\"header-mark\" aria-label=\"Header mark for 'Quando o cold boot ficou pequeno, o caminho quente ficou constrangedor'\"\u003e\u003c/a\u003e7 Quando o cold boot ficou pequeno, o caminho quente ficou constrangedor\u003c/h2\u003e\u003cp\u003eO restore de snapshot costumava ser o caminho rápido. Um restore de 80 ms em cima de um cold boot de 400 ms é um erro de arredondamento. Um restore de 80 ms em cima de um cold boot de 170 ms é metade do seu orçamento.\u003c/p\u003e\n\u003cp\u003eO restore antigo fazia a coisa óbvia: alocar um mmap anônimo fresco de 128 MiB para a RAM do guest, ler o arquivo de snapshot inteiro para um byte slice de Go, e fazer memcpy de tudo para a posição. Os passos um a três levavam cerca de 80 ms, exatamente como você esperaria se já tiver feito memcpy de 128 MiB a cada requisição.\u003c/p\u003e\n\u003cp\u003eEntão a pergunta: e se eu simplesmente não copiasse?\u003c/p\u003e\n\u003cp\u003eO Linux tem uma flag chamada \u003ccode\u003eMAP_PRIVATE\u003c/code\u003e. Quando você faz mmap de um arquivo com ela, o kernel não faz nenhum I/O real de antemão. Ele monta uma entrada de tabela de páginas que diz \u0026ldquo;se o userspace tocar nesta página, faça fault no kernel, leia do arquivo, mapeie. Se o userspace \u003cem\u003eescrever\u003c/em\u003e na página, faça fault, copy-on-write para uma página anônima privada, e redirecione o mapeamento para a cópia.\u0026rdquo; O arquivo em si nunca é modificado.\u003c/p\u003e\n\u003cp\u003eA analogia da Netflix é aquela à qual sempre volto. A Netflix não baixa o filme inteiro para o seu dispositivo primeiro para depois começar a tocar. Ela começa a tocar imediatamente e busca cada minuto enquanto você assiste. Se você pular partes adiante, essas partes nunca são baixadas. Você paga por minuto assistido, não por filme selecionado. O \u003ccode\u003eMAP_PRIVATE\u003c/code\u003e é esse padrão para a RAM do guest.\u003c/p\u003e\n\u003cp\u003eO novo caminho faz mmap do snapshot diretamente na região de memória do guest:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-10\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emem\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003e_\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eMmap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eFd\u003c/span\u003e\u003cspan class=\"p\"\u003e()),\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ememSize\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePROT_READ\u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePROT_WRITE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eMAP_PRIVATE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003e_\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eMadvise\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003emem\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eunix\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eMADV_HUGEPAGE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003ePáginas que o guest nunca toca nunca são carregadas. Páginas que ele lê mas não escreve permanecem compartilhadas com o page cache. Páginas que ele escreve vão para cópias COW privadas, e o arquivo de snapshot permanece limpo.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-11\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  BEFORE: eager copy\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +----------------+   +----------------+    +----------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |  mem.bin file  |--\u0026gt;|   os.ReadFile  |---\u0026gt;| copy(ram, mem) |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |    128 MiB     |   |  read 128 MiB  |    |  128 MiB memcpy|\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +----------------+   +----------------+    +----------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                      |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                                      v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                           ~80ms before this point\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  AFTER: lazy mmap (MAP_PRIVATE)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +----------------+   +----------------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |  mem.bin file  |\u0026lt;--| mmap(fd, PRIVATE)          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |    128 MiB     |   | sets up page table only    |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +----------------+   +----------------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        guest touches page N\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        minor fault (-\u0026gt; page cache)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        kernel maps the page on the fly\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                   v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                         ~20ms to \u0026#34;running\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA dança completa de page-fault por baixo dos panos é assim:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-12\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  guest vCPU                  host kernel (KVM + mm)        snapshot\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +---------+                                                +-------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | read P  |---(EPT miss)---\u0026gt;| PTE not-present, PRIVATE     | on    |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; minor fault                | disk  |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; page cache lookup          |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 |    (or read from disk)    \u0026lt;---+       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; install PTE readable       |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |\u0026lt;-----(resume)---|                               |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +---------+                                                 +-------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  later, guest writes page P:\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +---------+                                                 +-------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | write P |---(EPT miss)---\u0026gt;| PTE readable only             |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; COW fault                  |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; alloc anon page            |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; copy from page cache       |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 | -\u0026gt; install PTE writable       |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |                 |    (snapshot unchanged!)      |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  |         |\u0026lt;-----(resume)---|                               |       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +---------+                                                 +-------+\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eCada passo ali é o que o Linux já faz para qualquer mmap respaldado por arquivo. Nem uma única linha de tratamento de page-fault precisou ser implementada. Apenas pare de brigar com o kernel e peça que ele faça o seu trabalho.\u003c/p\u003e\n\u003cp\u003eNum snapshot Alpine de 128 MiB, o restore caiu de ~80 ms para cerca de 20 ms. O snapshot-resume ficou de repente várias vezes mais rápido que o cold boot. (Uma ressalva importante: não delete o arquivo de snapshot enquanto VMs estiverem rodando a partir dele. Pergunte como eu sei.)\u003c/p\u003e\n\u003ch2 id=\"mise-en-place-para-vms\" class=\"headerLink\"\u003e\n    \u003ca href=\"#mise-en-place-para-vms\" class=\"header-mark\" aria-label=\"Header mark for 'Mise en place para VMs'\"\u003e\u003c/a\u003e8 Mise en place para VMs\u003c/h2\u003e\u003cp\u003eEntre num restaurante decente na hora do almoço. Peça o steak frites. Ele chega em seis minutos. Só o bife já é um cozimento de seis minutos, estimativa generosa. As batatas levam doze. O bearnaise precisa de quinze. Como a cozinha conseguiu em seis minutos?\u003c/p\u003e\n\u003cp\u003eMise en place. As batatas estão pré-cozidas e escorridas antes de você chegar. O bearnaise está emulsionado e em espera. O prato saiu do aquecedor no momento em que o pedido chegou ao passe. A única coisa que a cozinha faz \u003cem\u003edepois do seu pedido\u003c/em\u003e é a selagem final.\u003c/p\u003e\n\u003cp\u003eUm warm pool é mise en place para VMs. O provedor de sandbox concorrente mais rápido no benchmark público ficava em torno de 100 ms — exatamente o que você esperaria de pular o restore inteiramente por ter uma VM já rodando, pausada, esperando alguém dizer vai. Se o líder ganha pré-cozinhando, pare de otimizar o fogão.\u003c/p\u003e\n\u003cp\u003eO warm pool virou três decisões de design, cada uma resultado de uma discussão com um dia ruim hipotético.\u003c/p\u003e\n\u003cp\u003ePrimeiro, \u003ccode\u003eAcquire\u003c/code\u003e é não-bloqueante. A tentação com APIs de pool é fazer o \u003ccode\u003eAcquire\u003c/code\u003e bloquear até que um worker esteja disponível. Esse \u0026ldquo;sempre dê um worker ao usuário\u0026rdquo; parece seguro. Não é. Se o pool está vazio, algo já deu errado, e fazer o usuário esperar por um restore fresco é estritamente pior do que cair para o caminho de cold-boot que já funciona. O pool é best-effort. Um miss nunca deve deixar o usuário mais lento que a baseline.\u003c/p\u003e\n\u003cp\u003eSegundo, liberar um worker o \u003cem\u003emata\u003c/em\u003e. Toda biblioteca de pooling eventualmente quer reciclar um worker de volta para o pool. Num mundo multi-tenant, o worker que acabou de tratar uma requisição tocou o que quer que o último tenant tenha pedido. Entregá-lo ao próximo tenant é um buraco de isolamento entre tenants, e o fato de ninguém tê-lo explorado ainda não é um argumento de segurança. Todo \u003ccode\u003eAcquire\u003c/code\u003e retorna um processo que nunca serviu uma requisição. O refill acontece em segundo plano, então o próximo chamador não paga nada. O pool está sempre em movimento. Nunca reutilizado.\u003c/p\u003e\n\u003cp\u003eTerceiro, o refill é assíncrono, limitado e seguro contra corridas. Uma rajada de requisições de refill para o mesmo template não deve debandar em dez spawns paralelos; um spawn de refill que corre com o shutdown deve se limpar sozinho; e um relógio deve ser injetável para que os testes de obsolescência sejam determinísticos. Nenhum disso é genial. São apenas os invariantes que você se arrepende de ter deixado de fora na primeira vez que o pool roda em produção.\u003c/p\u003e\n\u003cp\u003eO fluxo inteiro com o cache e o pool conectados:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-13\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  request arrives\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  warmcache.Lookup(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         +-- miss --\u0026gt; cold boot (~250ms)  \u0026lt;-- baseline\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  hit, snapshotDir=S\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  pool.Acquire(key, S)\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         +-- empty --\u0026gt; restore_direct (~20ms)   \u0026lt;-- still better\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  got a warm worker\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  worker.Resume (~3ms)  \u0026lt;-- fastest path\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  serve request\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  pool.Release(w)  --\u0026gt; worker.Close()\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  EnsureRefill in background\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  spawn replacement (~20ms off the hot path)\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA superfície de API do pool é pequena de propósito:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-14\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// pkg/warmpool/pool.go\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003eWorker\u003c/span\u003e \u003cspan class=\"kd\"\u003einterface\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eID\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eClose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003ePool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003eAcquire\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003esnapshotDir\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eWorker\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003ePool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003eRelease\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ew\u003c/span\u003e \u003cspan class=\"nx\"\u003eWorker\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003ePool\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003eEnsureRefill\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003esnapshotDir\u003c/span\u003e \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNo caminho quente: o worker quente já tem a RAM do guest mapeada, o estado da vCPU carregado, a VM pausada. \u003ccode\u003eAcquire\u003c/code\u003e retorna. Um único ioctl de resume o vira de pausado para rodando. Três milissegundos depois, o guest já disse olá. Mise en place.\u003c/p\u003e\n\u003ch2 id=\"nove-sandboxes-queimando-uma-cpu-à-toa\" class=\"headerLink\"\u003e\n    \u003ca href=\"#nove-sandboxes-queimando-uma-cpu-%c3%a0-toa\" class=\"header-mark\" aria-label=\"Header mark for 'Nove sandboxes queimando uma CPU à toa'\"\u003e\u003c/a\u003e9 Nove sandboxes queimando uma CPU à toa\u003c/h2\u003e\u003cp\u003eNove sandboxes estavam rodando, pausadas, ociosas. Sem tráfego. Sem sessões de exec. Sem HTTP. Sentadas num prompt de shell dentro de um warm pool, esperando alguém pedir que trabalhassem. O \u003ccode\u003etop\u003c/code\u003e mostrava o host em 46% de um core.\u003c/p\u003e\n\u003cp\u003eQuarenta e seis por cento para manter nove guests Linux ociosos vivos. Cerca de cinco por cento de um core por VM ociosa. Uma máquina Linux física ociosa usa em torno de 0,1% de um core em hardware moderno. Um guest ocioso devidamente virtualizado deveria ser \u003cem\u003emais barato\u003c/em\u003e, não cinquenta vezes mais caro.\u003c/p\u003e\n\u003cp\u003eAlgo estava muito errado.\u003c/p\u003e\n\u003cp\u003eA maneira limpa de ver o que uma thread de vCPU está fazendo é amostrá-la. Alguns segundos de \u003ccode\u003eperf\u003c/code\u003e devolveram um stack trace que era inequívoco: entra no KVM, sai quase imediatamente, dorme por um milissegundo, volta para dentro. Repetidamente, mil vezes por segundo, em cada thread de vCPU, em paralelo. Nove threads fazendo isso ao mesmo tempo eram exatamente os 370% de um core que o host estava reportando.\u003c/p\u003e\n\u003cp\u003eA causa era um hedge. Lá no fundo do loop da vCPU, a saída \u003ccode\u003eHLT\u003c/code\u003e estava sendo \u0026ldquo;tratada\u0026rdquo; com um sleep de um milissegundo:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-15\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"nx\"\u003eKVM_EXIT_HLT\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// Guest is idle. Don\u0026#39;t spin; give it a breather.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eSleep\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eMillisecond\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eO sleep era razoável num mundo onde o VMM é dono do controlador de interrupções no userspace. O gocracker não é. O gocracker usa o IRQCHIP in-kernel — o default certo para quase todo workload — onde o KVM deveria segurar a thread \u003cem\u003edentro\u003c/em\u003e do ioctl até a próxima interrupção disparar, sem nenhuma saída. O sleep era código morto que sobreviveu a uma mudança de design que ninguém questionou.\u003c/p\u003e\n\u003cp\u003eA correção foi uma deleção:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-16\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"nx\"\u003eKVM_EXIT_HLT\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// No-op. In-kernel IRQCHIP already blocks the vCPU until the\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"c1\"\u003e// next interrupt. There is no productive work for userspace here.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eNa próxima iteração do loop, o código chama o KVM de novo, e o KVM — porque é dono do IRQCHIP e sabe que não há interrupção a caminho — bloqueia a thread dentro do kernel por todo o tempo em que o guest permanecer ocioso.\u003c/p\u003e\n\u003cp\u003eMesmo teste de nove sandboxes ociosas. \u003ccode\u003etop\u003c/code\u003e: 7%. Não sete por cento por VM. Sete por cento para a frota inteira. De ~370% para ~7% deletando uma linha. Cinquenta vezes menos.\u003c/p\u003e\n\u003cp\u003eO padrão geral vale a pena nomear. O código que você adicionou \u0026ldquo;só por garantia\u0026rdquo; é frequentemente o código mais digno de ser deletado, porque ninguém o questiona. As partes de um sistema com que você briga são revisadas até a morte. As partes de que ninguém reclama apodrecem em paz. Quando a podridão finalmente te custa, ela te custa cinquenta vezes mais do que qualquer coisa em que você já tenha pensado.\u003c/p\u003e\n\u003ch2 id=\"uma-microvm-rápida-é-uma-caixa-de-ferramentas-não-um-produto\" class=\"headerLink\"\u003e\n    \u003ca href=\"#uma-microvm-r%c3%a1pida-%c3%a9-uma-caixa-de-ferramentas-n%c3%a3o-um-produto\" class=\"header-mark\" aria-label=\"Header mark for 'Uma microVM rápida é uma caixa de ferramentas, não um produto'\"\u003e\u003c/a\u003e10 Uma microVM rápida é uma caixa de ferramentas, não um produto\u003c/h2\u003e\u003cp\u003eQuando o tempo de \u003ccode\u003eAcquire\u003c/code\u003e até a primeira instrução do guest virou três milissegundos, fiquei orgulhoso disso por cerca de uma semana. Então tentei construir algo \u003cem\u003ecom\u003c/em\u003e aquilo.\u003c/p\u003e\n\u003cp\u003eO que eu queria era a coisa que todo mundo quer hoje em dia: uma API REST onde um cliente diz \u0026ldquo;me dê uma sandbox Python 3.12 com \u003ccode\u003enumpy\u003c/code\u003e e \u003ccode\u003epandas\u003c/code\u003e, deixe eu rodar código nela\u0026rdquo;, uma sandbox aparece, três segundos depois ele recebe um stdout de volta, e segue com a vida. Um \u003ccode\u003egocracker run\u003c/code\u003e cru não consegue fazer nada disso. Ele boota uma VM. É só isso. Se uma microVM é um bloco de motor, o que eu precisava era do resto do carro.\u003c/p\u003e\n\u003cp\u003eA primeira decisão foi a mais importante: manter o gocracker exatamente o que ele era, e construir a camada gerenciada como uma coisa separada. O gocracker permanece o VMM de baixo nível, o cache de snapshots, e o warm-pool-de-workers. Ele fala bytes e ioctls. Não tem opiniões sobre clientes ou templates. \u003cem\u003esandboxd\u003c/em\u003e é um novo daemon que fica acima dele e é dono de templates, leases, pools e tokens de preview. O SDK só fala com o sandboxd. O sandboxd só fala com o gocracker por um socket unix. O round-trip extra é uma feature, não um bug.\u003c/p\u003e\n\u003cp\u003eAprendi o valor dessa divisão primeiro \u003cem\u003enão\u003c/em\u003e a fazendo de forma limpa, e gastando três horas depurando uma condição de corrida que só existia porque duas camadas estavam compartilhando um ponteiro que não tinham por que compartilhar. Cruzar uma fronteira de processo te força a negociar. Compartilhar um ponteiro te deixa trapacear. A fronteira é o cinto de segurança.\u003c/p\u003e\n\u003cp\u003eA divisão é um fluxo limpo de três camadas:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-17\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eSDK\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ePython\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"n\"\u003eGo\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"n\"\u003eTS\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e  \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eover\u003c/span\u003e \u003cspan class=\"n\"\u003eunix\u003c/span\u003e \u003cspan class=\"n\"\u003esocket\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003ev\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003esandboxd\u003c/span\u003e            \u003cspan class=\"o\"\u003e\u0026lt;--\u003c/span\u003e \u003cspan class=\"n\"\u003emanaged\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003eruntime\u003c/span\u003e \u003cspan class=\"n\"\u003edaemon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e             \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etemplates\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eleases\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epools\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epreview\u003c/span\u003e \u003cspan class=\"n\"\u003etokens\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e  \u003cspan class=\"n\"\u003eHTTP\u003c/span\u003e \u003cspan class=\"n\"\u003eover\u003c/span\u003e \u003cspan class=\"n\"\u003eunix\u003c/span\u003e \u003cspan class=\"n\"\u003esocket\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003ev\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003egocracker\u003c/span\u003e \u003cspan class=\"n\"\u003eserve\u003c/span\u003e     \u003cspan class=\"o\"\u003e\u0026lt;--\u003c/span\u003e \u003cspan class=\"n\"\u003elow\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"n\"\u003eVMM\u003c/span\u003e \u003cspan class=\"n\"\u003eorchestrator\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e             \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eKVM\u003c/span\u003e \u003cspan class=\"n\"\u003eioctls\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003esnapshots\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ewarm\u003c/span\u003e \u003cspan class=\"n\"\u003epool\u003c/span\u003e \u003cspan class=\"n\"\u003eof\u003c/span\u003e \u003cspan class=\"n\"\u003eworkers\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e  \u003cspan class=\"n\"\u003eKVM\u003c/span\u003e \u003cspan class=\"n\"\u003eioctls\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003evsock\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"n\"\u003ev\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eguest\u003c/span\u003e \u003cspan class=\"n\"\u003eVM\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e|\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         \u003cspan class=\"o\"\u003e+--\u003c/span\u003e \u003cspan class=\"n\"\u003etoolbox\u003c/span\u003e \u003cspan class=\"n\"\u003eagent\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elistens\u003c/span\u003e \u003cspan class=\"n\"\u003eon\u003c/span\u003e \u003cspan class=\"n\"\u003ea\u003c/span\u003e \u003cspan class=\"n\"\u003evsock\u003c/span\u003e \u003cspan class=\"n\"\u003eport\u003c/span\u003e \u003cspan class=\"n\"\u003einside\u003c/span\u003e \u003cspan class=\"n\"\u003ethe\u003c/span\u003e \u003cspan class=\"n\"\u003eguest\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eTrês saltos. Dois daemons. O SDK nunca fala com o gocracker diretamente — ele não sabe que o gocracker existe. O sandboxd é a única superfície de API pública; tudo a jusante é um detalhe de implementação. Cruzar uma fronteira de processo num socket unix é barato (sub-milissegundo para payloads JSON pequenos), e a separabilidade se paga na primeira vez que você quer reiniciar o sandboxd sem matar uma centena de VMs vivas.\u003c/p\u003e\n\u003cp\u003eTemplates são a outra ideia de carga. Um cliente não quer \u0026ldquo;uma VM Linux.\u0026rdquo; Ele quer o ambiente que usa para o seu agente de IA — uma imagem base específica, alguns pacotes apt, alguns pacotes pip, um diretório de trabalho, algumas variáveis de ambiente. Um template captura essa mistura, \u003cem\u003emais\u003c/em\u003e o snapshot resultante de bootar a spec uma vez e deixá-la chegar a um estado estável. Dois templates com specs idênticas compartilham um snapshot. Um segundo create com a mesma spec é um no-op.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-18\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e \u003cspan class=\"nx\"\u003eTemplate\u003c/span\u003e \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eID\u003c/span\u003e           \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eName\u003c/span\u003e         \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eSnapshotDir\u003c/span\u003e  \u003cspan class=\"kt\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eSpecHash\u003c/span\u003e     \u003cspan class=\"kt\"\u003estring\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// canonical fingerprint of image, kernel, mem, env...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003eContextHash\u003c/span\u003e  \u003cspan class=\"kt\"\u003estring\u003c/span\u003e  \u003cspan class=\"c1\"\u003e// build-context tarball when using a Dockerfile\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003eWarmPolicy\u003c/span\u003e   \u003cspan class=\"nx\"\u003eWarmPolicy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eIsso parece óbvio até você imaginar o ciclo de vida de um SaaS de verdade: a maioria dos creates de template é retry idempotente. Um deploy roda de novo. Um job de CI ressubmete. Um SDK preguiçosamente garante-que-existe antes de um create. Se cada um deles custasse um \u003ccode\u003edocker build\u003c/code\u003e fresco, você estaria entregando um produto de $40/mês sobre $400/mês de infra. A identidade endereçável por conteúdo em cada camada se acumula: o warm cache era endereçável por conteúdo, os templates são endereçáveis por conteúdo em cima dele, e as sandboxes são baratas porque os templates são baratos.\u003c/p\u003e\n\u003ch2 id=\"cinco-quentes-e-prontas-nenhuma-estava\" class=\"headerLink\"\u003e\n    \u003ca href=\"#cinco-quentes-e-prontas-nenhuma-estava\" class=\"header-mark\" aria-label=\"Header mark for 'Cinco quentes e prontas. Nenhuma estava.'\"\u003e\u003c/a\u003e11 Cinco quentes e prontas. Nenhuma estava.\u003c/h2\u003e\u003cp\u003eEu estava rodando um teste de carga contra um sandboxd recém-reconstruído. Nada sofisticado — criar uma sandbox, executar \u003ccode\u003eecho hi\u003c/code\u003e, deletar a sandbox, num loop apertado. O pool estava configurado para três hot-ready e três paused-ready para um único template. Todo create deveria ser essencialmente instantâneo, porque o pool deveria manter seis sandboxes aquecidas vivas e eu só precisava de uma de cada vez.\u003c/p\u003e\n\u003cp\u003eFuncionou por cerca de noventa segundos.\u003c/p\u003e\n\u003cp\u003eEntão todo create começou a falhar. Não devagar. Não com backpressure. Cada um deles, com variações de \u0026ldquo;runtime returned 404: unknown vm.\u0026rdquo; O endpoint de status do pool reportava três hot-ready, duas paused-ready, zero leased. Um pool perfeitamente saudável, segundo ele mesmo. As VMs estavam mortas havia minutos.\u003c/p\u003e\n\u003cp\u003eFoi uma terça-feira divertida.\u003c/p\u003e\n\u003cp\u003eA primeira versão do reconciler confiava no seu próprio registro em memória. Ela contava entradas marcadas \u003ccode\u003ewarm_ready\u003c/code\u003e, comparava a contagem com \u003ccode\u003eMinHot\u003c/code\u003e, e concluía: saudável, nenhuma ação necessária. Nada no reconciler estava \u003cem\u003eolhando\u003c/em\u003e. Uma VM warm-ready morria silenciosamente — panic de vCPU, OOM-kill, travamento do guest, typo no fstab jogando o systemd no modo de rescue, qualquer um deles — e o sandboxd continuava contando-a como viva. Os leases subsequentes falhavam na hora do attach com 404s, o handler de lease marcava a entrada como \u0026ldquo;broken\u0026rdquo; e caía para o cold boot, mas as entradas quebradas persistiam na memória como \u003ccode\u003ewarm_leased\u003c/code\u003e até uma goroutine de cleanup separada recolhê-las. Enquanto isso, o pool continuava alegando cinco quentes, o reconciler continuava sem tomar decisões, e cada requisição fazia cold boot.\u003c/p\u003e\n\u003cp\u003eA cascata não foi espetacular. Sem alarmes de incêndio. Sem pager. O sistema estava silenciosamente se degradando ao modo de pior caso, um 404 de cada vez, enquanto diligentemente reportava verde.\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003etext\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-19\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  sandboxd\u0026#39;s view              actual runtime state\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +-----------------+          +----------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | warm_ready: 3   |          | VM #1: dead          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | warm_ready: 2   |          | VM #2: dead          |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | leased:     0   |          | VM #3: alive but oom |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  | total:      5   |          | VM #4: missing       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  +-----------------+          | VM #5: missing       |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         ^                     +----------------------+\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |                                  |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         | \u0026#34;healthy, no action\u0026#34;             | lease attempt -\u0026gt; 404\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |                                  |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e         |                                  v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   reconciler tick               lease handler marks broken,\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   counts in-memory state        falls through to cold boot\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   compares with MinHot                     |\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e   does nothing                             v\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                 user sees 2-second cold boot\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                                 every single request\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003eA degradação silenciosa é o pior modo. A falha barulhenta deixa você acionar um alerta sobre ela. A falha silenciosa significa que o gráfico parece verde enquanto os clientes vão embora.\u003c/p\u003e\n\u003cp\u003eA correção foi estrutural e pequena. O reconciler agora faz três coisas em ordem, e a ordem é de carga:\u003c/p\u003e\n\u003cdiv class=\"code-block highlight is-open show-line-numbers  tw-group tw-my-2\"\u003e\n  \u003cdiv class=\"\n    code-block-title \n    \n    tw-flex \n    tw-flex-row \n    tw-justify-between \n    tw-w-full tw-bg-bgColor-secondary\n    \"\u003e      \n    \u003cbutton \n      class=\"\n        tw-select-none \n        tw-mx-2 \n        tw-block\n        group-[.is-open]:tw-rotate-90\n        tw-transition-[transform] \n        tw-duration-500 \n        tw-ease-in-out\n        print:!tw-hidden\"\n      disabled\n      aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n    \u003cdiv class=\"code-block-title-bar tw-w-full\"\u003e\n      \u003cp class=\"tw-select-none !tw-my-1\"\u003ego\u003c/p\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"tw-flex\"\u003e\n      \u003cbutton \n        class=\"\n          line-number-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.show-line-numbers]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle line numbers\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n\n      \u003cbutton \n        class=\"\n          wrap-code-button\n          tw-select-none \n          tw-mx-2 \n          tw-hidden \n          group-[.is-open]:tw-block \n          group-[.is-wrap]:tw-text-fgColor-link \n          print:!tw-hidden\" \n        title=\"Toggle code wrap\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n      \n      \u003cbutton \n        class=\"\n          copy-code-button\n          tw-select-none\n          tw-mx-2 \n          tw-hidden\n          group-[.is-open]:tw-block\n          hover:tw-text-fgColor-link \n          print:!tw-hidden\"\n        title=\"Copy code\"\u003e\n          \u003cspan class=\"copy-icon tw-block\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n          \u003cspan class=\"check-icon tw-hidden\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z\"/\u003e\u003c/svg\u003e\u003c/span\u003e\n      \u003c/button\u003e\n        \n      \u003cbutton \n        class=\"\n          tw-select-none \n          tw-mx-2 \n          tw-block \n          group-[.is-open]:tw-hidden \n          print:!tw-hidden\" \n        disabled\n        aria-hidden=\"true\"\u003e\u003csvg class=\"icon\"\n    xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"\u003e\u003c!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --\u003e\u003cpath d=\"M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z\"/\u003e\u003c/svg\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cpre style=\"counter-reset: codeblock;\" class=\"tw-block tw-m-0 tw-p-0\"\u003e\u003ccode \n    id=\"codeblock-id-20\" \n    class=\"\n      chroma \n      !tw-block \n      tw-p-0\n      tw-m-0\n      tw-transition-[max-height] \n      tw-duration-500 \n      tw-ease-in-out \n      group-[.is-closed]:!tw-max-h-0 \n      group-[.is-wrap]:tw-text-wrap\n      tw-overflow-y-hidden\n      tw-overflow-x-auto\n      tw-scrollbar-thin\n      \"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eManager\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nf\"\u003ereconcileTemplate\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etpl\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eTemplate\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereapDead\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etpl\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e              \u003cspan class=\"c1\"\u003e// probe runtime, drop ghosts\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003einv\u003c/span\u003e \u003cspan class=\"o\"\u003e:=\u003c/span\u003e \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003einventoryFor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etpl\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eID\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// count from honest state\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e    \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003epruneExcess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etpl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003einv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereplenishUpToMin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etpl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003einv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp\u003ePrimeiro, sondar cada sandbox quente que o manager acha que possui e recolher qualquer coisa que o runtime não conheça mais — \u0026ldquo;inconclusivo\u0026rdquo; conta como morto, porque um pool de VMs talvez-vivas é pior que um pool com um buraco. Segundo, contar a partir do inventário agora honesto. Terceiro, aparar o excesso e repor até o mínimo. Antes da correção: cinco sandboxes fantasma, cada requisição um cold boot, o pool alegremente reportando saudável. Depois: creates instantâneos de novo.\u003c/p\u003e\n\u003cp\u003eJá bati nesse exato bug antes. Suspeito que você também. Toda vez ele veste uma roupa ligeiramente diferente — um controller do Kubernetes que confia no status de pod em cache em vez do kubelet, um connection pool que marca um backend como saudável porque a última resposta foi 200 enquanto o socket recebeu FIN há trinta segundos, um service registry cuja thread de heartbeat não tem relação com a thread de trabalho, então o serviço pode estar em deadlock e ainda pingando, um navegador que cacheia um registro DNS além da realidade. O erro subjacente é o mesmo toda vez: confiar numa representação em memória do mundo, através de uma fronteira de processo, sem sondar. O estado em memória e a realidade fora do processo sempre divergem. A pergunta não é se você vai notar; é \u003cem\u003equando\u003c/em\u003e, e que dano visível ao usuário se acumula nesse meio-tempo.\u003c/p\u003e\n\u003cp\u003eMais duas guardas entraram na mesma época. Um backoff por template para que um único template quebrado — digamos, um cujo snapshot está sutilmente corrompido — não consiga sozinho manter o reconciler grudado fazendo spawn de VMs que falham a cada tick, faminteando templates mais saudáveis. E um orçamento global de trabalho de spawn em voo por todo o host, porque dez templates cada um querendo repor três VMs ao mesmo tempo são trinta spawns paralelos, o que é o bastante para tornar cada spawn mais lento do que precisa, o que aperta os timeouts, o que cascateia. Limites por template não bastam. O número de coisas que podem dar errado simultaneamente através de N templates cresce mais rápido do que o limite por template o contém.\u003c/p\u003e\n\u003ch2 id=\"o-que-a-fundação-me-ensinou\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-que-a-funda%c3%a7%c3%a3o-me-ensinou\" class=\"header-mark\" aria-label=\"Header mark for 'O que a fundação me ensinou'\"\u003e\u003c/a\u003e12 O que a fundação me ensinou\u003c/h2\u003e\u003cp\u003eOlhando para trás no arco inteiro, algumas coisas se destacam o suficiente para valer a pena carregar adiante.\u003c/p\u003e\n\u003cp\u003eO estado do kernel do host sobrevive ao seu processo. Limpe-o na inicialização tanto quanto no shutdown. \u003ccode\u003eclose(fd)\u003c/code\u003e é uma operação de refcount, não uma mensagem de protocolo — se você precisa que o peer saiba que você foi embora, você tem que de fato dizer. Todo caminho de saída precisa de um restore terminal, porque defer é uma sugestão que sinais e trips de seccomp ignoram. O detector de corrida no CI é inegociável para qualquer projeto Go que mantenha estado entre goroutines.\u003c/p\u003e\n\u003cp\u003eO seu maior custo quase certamente não é a coisa que você escreveu. O Linux bootando dentro da VM eram três quartos de um cold start de quatrocentos milissegundos. Nada do que eu escrevi importou até eu ir lá e encolher aquilo. Uma UART virtualizada é cara por byte; silenciar o log do kernel no caminho do console foi a maior vitória de performance do projeto. O \u003ccode\u003eMAP_PRIVATE\u003c/code\u003e é dinheiro grátis para o restore de snapshot. O garbage collector do Go é um imposto que você pode escolher não pagar em subprocessos de vida curta.\u003c/p\u003e\n\u003cp\u003eConfie no kernel mais do que nos seus instintos. O IRQCHIP in-kernel já resolve o estacionamento de vCPU ociosa. O sleep defensivo em cima era trabalho negativo. Código defensivo é um detector de mentiras para premissas que mudaram desde então: revisite os hedges quando o sistema subjacente mudar. E às vezes a maior vitória é uma deleção.\u003c/p\u003e\n\u003cp\u003eQuando um warm pool sobe acima de uma microVM, as regras mudam. O pool é best-effort — um miss nunca deve deixar o usuário mais lento que a baseline. Mate os workers no release; nunca dê a um tenant um processo que tocou os dados de outro tenant. Loops de reconciler devem observar antes de agir, porque a única coisa mais perigosa que um cache errado é um cache que o sistema parou de questionar. E inconclusivo é sempre morto num pool — o custo de tratar uma sandbox talvez-viva como morta é um cold boot; o custo de tratar uma morta como viva é a falha de lease que o seu cliente vê.\u003c/p\u003e\n\u003cp\u003eNenhuma dessas vitórias é individualmente genial. Cada uma é algo que outra pessoa descobriu anos atrás — mmap, copy-on-write, isolamento por tenant, eventfd mais IRQFD, mise en place como conceito, confiar no scheduler do IRQCHIP in-kernel. Nada inventado aqui. O que aconteceu foi parar de brigar com cada um deles, um de cada vez. É assim que cold starts de ~3 ms visíveis ao usuário acontecem. Você os conquista camada por camada. Não há uma única mudança heroica. Há uma pilha de mudanças pequenas e honestas, cada uma das quais torna a próxima mais barata de escrever.\u003c/p\u003e\n\u003cp\u003eAs partes interessantes da máquina estão prontas. O que resta é mantê-las honestas.\u003c/p\u003e\n",
        "language": "pt"
    },
    {
        "title" : "Memoirs: Ensinando Agentes a Lembrar (Sem Perder a Cabeça na Nuvem)",
        "date_published" : "2026-05-12T00:00:00Z",
        "date_modified" : "2026-05-12T00:00:00Z",
        "id" : "https://misael.org/pt/memoirs-local-first-memory/",
        "url" : "https://misael.org/pt/memoirs-local-first-memory/",
        "summary": "Por que concatenar transcrições de chat não escala, e como construí um sistema de memória de longo prazo probabilístico e local-first sobre o SQLite.",
        "content_html" : "\u003ch2 id=\"a-amnésia-de-mil-tokens\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-amn%c3%a9sia-de-mil-tokens\" class=\"header-mark\" aria-label=\"Header mark for 'A Amnésia de Mil Tokens'\"\u003e\u003c/a\u003e1 A Amnésia de Mil Tokens\u003c/h2\u003e\u003cp\u003eTodo agente conversacional com que já trabalhei — Claude, Cursor, vários loops de CLI — compartilha a mesma falha silenciosa. Eles acordam sem nenhuma memória de ontem. As janelas de contexto ficaram enormes, mas eles ainda começam de uma página em branco a cada nova sessão. Se você tenta contornar isso mantendo uma thread contínua, acaba com algo pior: uma monstruosidade arquitetural que despeja todo o histórico do chat na janela de contexto a cada turno. Você paga o custo de inferência de 40K tokens, e o agente ainda enterra o fato mais importante em algum lugar no meio — invisível, ignorado. Esse é o efeito \u003cem\u003eLost in the Middle\u003c/em\u003e. É caro e nem sequer funciona.\u003c/p\u003e\n\u003cp\u003eExistem soluções corporativas para isso. Pesadas. Bancos de dados vetoriais hospedados na nuvem com idas e voltas pela rede medidas em segundos num dia bom. Mas eu não vou enviar minhas notas de arquitetura, minhas credenciais meio censuradas e minhas convenções pessoais para alguma API externa que não controlo. Eu queria algo \u003cem\u003elocal-first\u003c/em\u003e. Privado por construção. Algo que de fato entendesse minhas variáveis de ambiente, minhas escolhas de framework, meu estilo — e mantivesse esse conhecimento por perto.\u003c/p\u003e\n\u003cp\u003eFoi isso que se tornou o \u003ca href=\"https://github.com/misaelzapata/memoirs\" target=\"_blank\" rel=\"noopener noreferrer\"\u003ememoirs\u003c/a\u003e. Não um wrapper de RAG. Isso teria sido fácil demais, e entediante demais. Eu queria um sistema de memória de verdade — um que esquece organicamente e consolida o que realmente importa.\u003c/p\u003e\n\u003ch2 id=\"a-anatomia-subjacente-6-camadas-sobre-o-sqlite\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-anatomia-subjacente-6-camadas-sobre-o-sqlite\" class=\"header-mark\" aria-label=\"Header mark for 'A Anatomia Subjacente: 6 Camadas sobre o SQLite'\"\u003e\u003c/a\u003e2 A Anatomia Subjacente: 6 Camadas sobre o SQLite\u003c/h2\u003e\u003cp\u003eConstruí a camada de persistência sobre algo que a maioria das pessoas subestima: o SQLite. Não só um arquivo \u003ccode\u003e.db\u003c/code\u003e plano. Uma instância de SQLite turbinada com \u003ccode\u003esqlite-vec\u003c/code\u003e para busca vetorial densa e \u003ccode\u003eFTS5\u003c/code\u003e nativo para busca lexical por índice invertido com pontuação BM25.\u003c/p\u003e\n\u003cp\u003eA arquitetura se assentou em seis camadas, porque um sistema de memória não é uma caixa de armazenamento — é uma estação de tratamento de água:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eLogs brutos\u003c/strong\u003e: O tubo de entrada. Recebe tudo — histórico completo de conversa, diffs ruidosos, saída bruta.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eExtração\u003c/strong\u003e: Um pequeno LLM curador rodando localmente (atualmente Qwen 2.5 3B — rápido o suficiente para não se notar) que filtra sinal do ruído: heurísticas, credenciais, preferências, marcadores de estilo.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGrafo\u003c/strong\u003e: O tecido conjuntivo. As memórias são interligadas usando princípios no estilo Zettelkasten, encontrando nós semânticos compartilhados entre conceitos e sessões.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIndexação Dupla\u003c/strong\u003e: Escritas atômicas paralelas no sqlite-vec e no FTS5. Ambos os índices permanecem sincronizados.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMotor de Memória\u003c/strong\u003e: A camada de curadoria. As memórias ganham e perdem pontuação ao longo do tempo, com suporte bi-temporal completo — você pode consultar o que o sistema acreditava em qualquer momento passado.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSuperfície\u003c/strong\u003e: A camada de exposição. HTTP + REST, e mais importante, um servidor MCP de 22 endpoints que permite a qualquer agente consultar o sistema de forma limpa sem saber nada sobre as entranhas.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"rrf-e-bm25-vetores-não-resolvem-tudo\" class=\"headerLink\"\u003e\n    \u003ca href=\"#rrf-e-bm25-vetores-n%c3%a3o-resolvem-tudo\" class=\"header-mark\" aria-label=\"Header mark for 'RRF e BM25: Vetores Não Resolvem Tudo'\"\u003e\u003c/a\u003e3 RRF e BM25: Vetores Não Resolvem Tudo\u003c/h2\u003e\u003cp\u003eNo começo, bati numa parede. Os embeddings do \u003ccode\u003esentence-transformers\u003c/code\u003e falhavam de um jeito muito específico: a recuperação exata de identificadores. Peça ao \u003ccode\u003ememoirs\u003c/code\u003e para recuperar um nome de ferramenta específico ou uma string de dependência versionada e ele voltava com ruído semanticamente adjacente. Os espaços vetoriais são brilhantes com significado. Eles sabem que \u0026ldquo;carro\u0026rdquo; está perto de \u0026ldquo;veículo\u0026rdquo;. Mas quando uma memória contém \u003cem\u003e\u0026ldquo;o sistema antigo usava psql-driver-v9\u0026rdquo;\u003c/em\u003e, o embedding não dá nenhum peso especial a essa string exata. Para um agente tentando reconstruir uma decisão, isso é um erro crítico.\u003c/p\u003e\n\u003cp\u003eA solução foi combinar algo novo com algo muito antigo: Reciprocal Rank Fusion (RRF). Rodamos as duas buscas em paralelo — uma consulta semântica densa pelo sqlite-vec, e uma consulta de índice invertido por correspondência exata pelo FTS5 com pontuação BM25. Se o BM25 encontra a string exatamente e a busca semântica entende o contexto ao redor, o RRF estabiliza as pontuações e empurra o resultado certo firmemente para a posição um. O resultado: a latência de recuperação p50 caiu para cerca de 3,9 ms. Num banco de dados SQLite local. Isso supera a maioria dos armazenamentos vetoriais na nuvem por uma boa margem.\u003c/p\u003e\n\u003ch2 id=\"ebbinghaus-curvas-de-esquecimento-e-sono-assíncrono\" class=\"headerLink\"\u003e\n    \u003ca href=\"#ebbinghaus-curvas-de-esquecimento-e-sono-ass%c3%adncrono\" class=\"header-mark\" aria-label=\"Header mark for 'Ebbinghaus, Curvas de Esquecimento e Sono Assíncrono'\"\u003e\u003c/a\u003e4 Ebbinghaus, Curvas de Esquecimento e Sono Assíncrono\u003c/h2\u003e\u003cp\u003eUm sistema não tem memória de longo prazo se não consegue esquecer. Os agentes têm tendência a se afogar em conhecimento obsoleto. \u0026ldquo;Ontem eu estava brigando com a API do Docker\u0026rdquo; seguido imediatamente de \u0026ldquo;Hoje abandonamos o Docker por completo e estamos em chamadas OCI puras.\u0026rdquo; Se o motor de memória traz à tona ambos os fatos com confiança igual, o agente vai tomar a decisão errada. Toda vez.\u003c/p\u003e\n\u003cp\u003eVoltei a um caderno antigo e implementei funções de decaimento baseadas na curva de esquecimento de Hermann Ebbinghaus: $R(t) = e^{-\\Delta t_h / (S \\cdot 24)}$. As memórias novas perdem peso assintoticamente com o tempo. Mas a pontuação de Força delas — a qualidade do sinal original — é multiplicada cada vez que o sistema recupera e confirma positivamente aquela memória. Decisões arquiteturais centrais e preferências profundas decaem muito mais devagar do que um relato de guerra de depuração de uma única sessão de madrugada.\u003c/p\u003e\n\u003cp\u003eA computação que isso exige — consolidação, resolução de conflitos semânticos, compactação de grafo — é cara demais para rodar em tempo real. Então recorri a um mecanismo que os humanos usam constantemente, mas que o software quase nunca usa: o \u003cem\u003esono\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eEscrevi um daemon (\u003ccode\u003esleep_consolidation.py\u003c/code\u003e) que vigia os vales de CPU. Ele só roda quando o desenvolvedor e os agentes se afastaram — literalmente as janelas de ociosidade em que o lock está livre e a ociosidade do sistema esteve acima de N minutos. Nessa janela assíncrona cega, o modelo Qwen ou Phi local acorda, revisa os perfis armazenados recentemente, caça contradições entre versões de memória e consolida ou arquiva as obsoletas. O grafo é compactado silenciosamente em segundo plano antes da próxima sessão começar.\u003c/p\u003e\n\u003cp\u003eNo fim do ciclo de benchmarking, alcançar pontuações fortes de MRR em benchmarks de recuperação de memória como o LoCoMo — com um overhead de RAM de apenas 231 MB, comparado ao que o LlamaIndex ou o Mem0 consomem — confirmou a hipótese. Os melhores recursos de agentes não são construídos enviando milhões de tokens para provedores remotos. Eles são construídos entendendo índices básicos a fundo e impondo uma disciplina de recursos implacável na sua própria máquina.\u003c/p\u003e\n",
        "language": "pt"
    },
    {
        "title" : "node-vmm: isolamento de VM que parece criar um processo",
        "date_published" : "2026-05-05T00:00:00Z",
        "date_modified" : "2026-05-05T00:00:00Z",
        "id" : "https://misael.org/pt/node-vmm-instant-vms/",
        "url" : "https://misael.org/pt/node-vmm-instant-vms/",
        "summary": "1 A mentira confortável sobre containersExiste uma ficção educada no desenvolvimento moderno que todo mundo aceita porque é conveniente: fingimos que os containers Docker são rápidos e leves. E são, se o seu ponto de referência for provisionar bare metal em 2005. Mas à medida que minhas ferramentas precisavam isolar cargas de trabalho com mais frequência — e de forma mais dinâmica — comecei a sentir o peso de depender de um motor externo gigante posicionado entre mim e aquilo que eu realmente queria.",
        "content_html" : "\u003ch2 id=\"a-mentira-confortável-sobre-containers\" class=\"headerLink\"\u003e\n    \u003ca href=\"#a-mentira-confort%c3%a1vel-sobre-containers\" class=\"header-mark\" aria-label=\"Header mark for 'A mentira confortável sobre containers'\"\u003e\u003c/a\u003e1 A mentira confortável sobre containers\u003c/h2\u003e\u003cp\u003eExiste uma ficção educada no desenvolvimento moderno que todo mundo aceita porque é conveniente: fingimos que os containers Docker são rápidos e leves. E são, se o seu ponto de referência for provisionar bare metal em 2005. Mas à medida que minhas ferramentas precisavam isolar cargas de trabalho com mais frequência — e de forma mais dinâmica — comecei a sentir o peso de depender de um motor externo gigante posicionado entre mim e aquilo que eu realmente queria.\u003c/p\u003e\n\u003cp\u003eO que eu queria era algo que \u003cem\u003eparecesse\u003c/em\u003e ergonomicamente como chamar \u003ccode\u003echild_process.spawn()\u003c/code\u003e no Node.js, mas que entregasse o isolamento de hardware de uma máquina virtual completa. Sem containers com namespaces compartilhados. Eu queria um kernel de verdade — ou pelo menos um micro-kernel — rodando isolado. E, mais do que tudo, eu queria que pausar e retomar fosse rápido o suficiente para que uma API HTTP dentro da VM pudesse responder a um cliente e parecer mera latência de rede. Sem atrasos de \u0026ldquo;descongelamento\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eFoi daí que veio o \u003ca href=\"https://github.com/misaelzapata/node-vmm\" target=\"_blank\" rel=\"noopener noreferrer\"\u003enode-vmm\u003c/a\u003e. E, como essas coisas costumam acontecer, a ideia era simples até o momento em que eu bati de cara com a realidade dos hypervisors nativos.\u003c/p\u003e\n\u003ch2 id=\"pulando-o-qemu-do-jeito-difícil\" class=\"headerLink\"\u003e\n    \u003ca href=\"#pulando-o-qemu-do-jeito-dif%c3%adcil\" class=\"header-mark\" aria-label=\"Header mark for 'Pulando o QEMU do jeito difícil'\"\u003e\u003c/a\u003e2 Pulando o QEMU do jeito difícil\u003c/h2\u003e\u003cp\u003eQuase todo projeto que precisa de virtualização multiplataforma recorre ao QEMU por instinto. Ele é o canivete suíço. Mas o QEMU é \u003cem\u003egrande\u003c/em\u003e. Envolver o QEMU a partir do Node teria resolvido o problema multiplataforma em uma semana — e matado completamente os objetivos de latência e peso.\u003c/p\u003e\n\u003cp\u003eEntão, em vez disso, eu o construí conversando diretamente com a API do hypervisor nativo de cada sistema operacional, usando C++ vinculado ao Node via N-API. O resultado é uma arquitetura dividida em três mundos completamente separados que não compartilham nenhum código na camada mais baixa:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eLinux\u003c/strong\u003e: chamadas \u003ccode\u003eioctl\u003c/code\u003e diretas ao KVM. Um arquivo com uma certa elegância bruta — \u003ccode\u003enative/kvm/backend.cc\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWindows\u003c/strong\u003e: a Windows Hypervisor Platform (WHP). Essa foi genuinamente dolorosa. A WHP te entrega uma CPU virtual nua e diz boa sorte para montar a placa-mãe, então tive que emular APIC, timers e portas UART do zero.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003emacOS / Apple Silicon\u003c/strong\u003e: Hypervisor.framework (HVF). Depois de várias madrugadas, percebi que o caminho mais limpo não era fingir ser x86, mas usar um perfil de máquina ARM64 (baseado em virt) para manter tudo rápido e nativo.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eEsconder os três por trás de uma única \u003ccode\u003einterface NativeRunConfig\u003c/code\u003e em TypeScript não foi trivial. O layout de interrupções MMIO para o Virtio foi onde as coisas ficaram confusas. No KVM, o passo de memória para dispositivos é limpo — 0x1000. No Windows, tive que compactá-los de forma mais apertada em 0x200 para evitar sobreposição com tabelas ACPI. No fim, a abstração se sustentou, e qualquer desenvolvedor que importe a biblioteca nunca precisa pensar em nada disso.\u003c/p\u003e\n\u003ch2 id=\"demitindo-o-intermediário-oci-sem-o-docker-engine\" class=\"headerLink\"\u003e\n    \u003ca href=\"#demitindo-o-intermedi%c3%a1rio-oci-sem-o-docker-engine\" class=\"header-mark\" aria-label=\"Header mark for 'Demitindo o intermediário: OCI sem o Docker Engine'\"\u003e\u003c/a\u003e3 Demitindo o intermediário: OCI sem o Docker Engine\u003c/h2\u003e\u003cp\u003eToda ferramenta moderna precisa rodar imagens. O movimento óbvio era criar uma ponte com o socket local do Docker. Isso quebrava minha regra de não usar motores pesados.\u003c/p\u003e\n\u003cp\u003eEntão escrevi o \u003ccode\u003eoci.ts\u003c/code\u003e — um cliente completo de registry OCI (Open Container Initiative) em TypeScript. Ele faz o parsing de manifests, negocia tokens e baixa blobs tar.gz camada por camada, injetando-os diretamente em um rootfs ext4 que a VM pode montar na hora. Inicializar \u003ccode\u003enode:22-alpine\u003c/code\u003e decodificando a imagem localmente e montando-a sem tocar no dockerd muda o que significa \u0026ldquo;instantâneo\u0026rdquo; na prática. Em arquiteturas onde o \u003ccode\u003emkfs.ext4\u003c/code\u003e não vem instalado por padrão, recorremos ao WSL2 ou ao Homebrew de forma graciosa, sem quebrar o fluxo.\u003c/p\u003e\n\u003ch2 id=\"o-momento-eureca-sharedarraybuffer-como-uma-cola-que-desafia-a-física\" class=\"headerLink\"\u003e\n    \u003ca href=\"#o-momento-eureca-sharedarraybuffer-como-uma-cola-que-desafia-a-f%c3%adsica\" class=\"header-mark\" aria-label=\"Header mark for 'O momento eureca: SharedArrayBuffer como uma cola que desafia a física'\"\u003e\u003c/a\u003e4 O momento eureca: SharedArrayBuffer como uma cola que desafia a física\u003c/h2\u003e\u003cp\u003eTempos de cold boot de 1 a 3 segundos eram aceitáveis. Minha obsessão real era fazer com que processos pausados retomassem dentro da latência de uma requisição de rede — sub-100 ms.\u003c/p\u003e\n\u003cp\u003eA abordagem tradicional para pausar/retomar uma VM é congelar a CPU, serializar a RAM e o estado de interrupções em disco e restaurar ao acordar. Isso é lento demais por ordens de magnitude para o que eu precisava. A outra opção era passagem de mensagens entre a thread principal do Node.js e a thread Worker que gerencia o hypervisor. O problema: a ponte de passagem de mensagens do Node sobre o event loop introduz jitter de latência e travamentos.\u003c/p\u003e\n\u003cp\u003eA sacada veio ao pensar em como os motores de jogos modernos renderizam: \u003cstrong\u003eSharedArrayBuffer\u003c/strong\u003e combinado com Atomics.\u003c/p\u003e\n\u003cp\u003eImplementei um pequeno buffer estruturado com slots para \u003ccode\u003eCONTROL_COMMAND\u003c/code\u003e, \u003ccode\u003eCONTROL_STATE\u003c/code\u003e e o console — algo que tanto a thread principal em TypeScript quanto a thread Worker em C++ conseguem ler atomicamente sem locks caros ou qualquer serialização de mensagens através do V8.\u003c/p\u003e\n\u003cp\u003eQuando quero pausar uma VM, a thread TS escreve atomicamente um \u003ccode\u003e1\u003c/code\u003e no buffer. O Worker do KVM/WHP, durante um de seus microscópicos VM-exits, verifica aquele byte compartilhado e simplesmente interrompe a execução da vCPU — sem derrubar a infraestrutura de memória da máquina. A VM não está em disco. Ela continua viva no hypervisor, apenas adormecida, sem queimar ciclos.\u003c/p\u003e\n\u003cp\u003eServidores Fastify ou Express lá dentro retomam e resolvem um \u003ccode\u003eGET /\u003c/code\u003e pendente em \u003cstrong\u003e5 a 50 milissegundos\u003c/strong\u003e. É um truque modesto comparado ao que um hypervisor comercial faz. O ganho ergonômico para subir ambientes isolados é tudo, menos modesto.\u003c/p\u003e\n\u003cp\u003eO projeto não está terminado. A restauração completa da RAM a partir de um snapshot frio ainda é algo que persigo com rastreamento de dirty-pages, que já deixei conectado nas fundações do código. Mas, até agora, consegui exatamente o que me propus a construir: o isolamento intransigente de uma máquina virtual de verdade, escondido dentro de algo que parece, opera e morre com a mesma facilidade de mais um processo no meu terminal.\u003c/p\u003e\n",
        "language": "pt"
    },
    ]
}
