[{"content":"Eram 2 da manhã. Eu tava fuçando na minha pasta de anime tentando entender por que o media server não tinha pego os metadados de um episódio novo. Abri o banco de dados SQLite por curiosidade — só pra ver o que tinha lá dentro.\nDava pra ler. Tudo. Cada título. Cada nota. Cada entrada do meu histórico. Texto puro, sem criptografia nenhuma, sentado ali no meu disco.\nFechei o terminal e fiquei olhando pro teto.\nÉ isso. Essa é a história inteira de por que tô construindo isso.\nO Estado do Media Self-Hosted Você tem o Plex, que começou como projeto comunitário e foi virando lentamente um produto que te pede pra fazer login nos servidores deles só pra assistir seus arquivos na sua máquina. Você tem o Jellyfin — genuinamente bom, mantido pela comunidade — mas em C#, com uma arquitetura que complica coisas que não precisavam ser complicadas. Você tem Navidrome pra música, Kavita pra livros, Komga pra manga. Ferramentas ótimas. Mas cada uma é um silo.\nVocê assiste anime num app. Lê manga em outro. Ouve música num terceiro. Cada um com seu próprio banco de dados, seu próprio sistema de auth, sua própria ideia de como é a sua biblioteca. E nenhum deles leva segurança a sério do jeito que eu acho que merece.\nNenhum deles criptografa seus metadados por padrão.\nPensa nisso por um segundo. Seu histórico de visualização. Suas notas. Os títulos de tudo que você já consumiu. Num arquivo SQLite em plaintext no seu disco. Se alguém roubar seu HD — ou tiver uns minutinhos sem supervisão perto do seu PC — tem um retrato completo do seu consumo de mídia sem esforço nenhum.\nIsso me incomoda.\nO Que Eu Tô Construindo Psyche é um media server local-first, de alta performance e alta segurança, escrito em Rust.\nAnime. Manga. Séries. Livros. Música. Um servidor. Uma API. Um frontend.\nO nome vem de Serial Experiments Lain (1998). No SEL, o Psyche é o eu — privado, local, criptografado. A fronteira entre o que é seu e o que pertence à rede. É exatamente isso que esse servidor é. Seus dados. Sua máquina. Não é assunto de mais ninguém.\nMas vai além: o histórico é criptografado no disco. Os metadados são criptografados no disco. As miniaturas são criptografadas no disco. Quem conseguir acesso ao sistema de arquivos vai encontrar texto cifrado. O servidor descriptografa em tempo real — plaintext nunca toca o disco.\nE quando eu digo local-first, quero dizer no sentido paranoico. Toda funcionalidade remota — AniList sync, APIs de metadados, tradução por LLM, busca de letras — tem uma alternativa local. Dá pra rodar o Psyche com zero conexões externas e não perder nada além de conveniência. Dá pra importar o dump do Kitsu (o time compartilha livremente) e ter metadados completos de anime offline pra sempre.\nA Stack (e Por Que Isso Importa) Rust. Axum. SQLite via sqlx. Esse é o core.\nEscolhi Rust porque precisava que o compilador fosse chato. Sério. Quando você tá construindo algo que lida com dados criptografados, faz streaming e caminha pelo sistema de arquivos, você quer um compilador que fala \u0026ldquo;não\u0026rdquo; com frequência. Quero saber em tempo de compilação que não introduzi buffer overflow, corrida de dados ou null dereference. O compilador do Rust é verboso e chato — e é exatamente isso que você quer quando segurança é o core da proposta de valor.\nO banco é SQLite. Não Postgres. Não document store. SQLite, porque isso é um servidor local-first e me recuso a te obrigar a rodar um daemon de banco só pra assistir seu próprio anime. Async-nativo via sqlx, com verificação de query em tempo de compilação — o SQL é validado contra o schema real antes de compilar. Sem mágica, sem ORM escondendo nada. Migrations em SQL puro em arquivos .sql numerados. Totalmente auditável.\nA camada HTTP é Axum — Tower-nativo, composável, mantido pelo time do Tokio. Sem surpresa.\nO Ecossistema Próprio Isso não é só um servidor. É o começo de um ecossistema. No SEL, o NAVI da Lain é o hardware e software que ela usa pra se conectar à Wired. Cada crate nesse projeto é uma camada — um pedaço da stack que deixa o Psyche fazer o que faz.\nNAVI: Ela faz um upgrade insano no NAVI dela — e é essa energia que tô trazendo pra essa codebase: Você já conhece o zantetsu — meu parser de nomes de arquivo de anime que escrevi alguns meses atrás. É a Layer 00. O core do scanner do Psyche. Ele lida com o caos: [SubsPlease] Jujutsu Kaisen - 24 (1080p) [A1B2C3D4].mkv, One Punch Man S02E03 1080p WEBRip x264.mkv, tudo isso. 92%+ de precisão só no parser heurístico, com fallback neural DistilBERT + CRF pros casos realmente bizarros.\nMas ainda tô construindo mais duas crates que não existem ainda:\npsyche-translate (Layer 01) é um motor de tradução com modelo de provider plugável. Providers remotos (OpenAI, Anthropic, Gemini), locais via Ollama ou llama.cpp, ou nada. A crate mantém contexto do episódio entre segmentos pra os nomes dos personagens ficarem consistentes. Dá pra traduzir uma trilha de legenda inteira, uma página de manga ou um capítulo de livro com uma chamada só — e o provider é só configuração.\nfill-metadata (Layer 02) é a ambiciosa. É um pipeline de machine learning que analisa arquivos de mídia brutos e infere metadados quando não existe nenhum. Análise de cena, fingerprinting de áudio, speech-to-text pra inferir título. Se você tem uma pasta cheia de vídeos sem nome de anos atrás, o fill-metadata propõe o que eles são. Você revisa, aprova, ele salva. Sem mágica. Sem commit automático. Você tá sempre no controle.\nO Pipeline de OCR É aqui que fica interessante.\nLegendas PGS — o formato baseado em bitmap que os Blu-rays usam — são imagens. Você não consegue pesquisar, traduzir ou copiar texto delas. O Psyche vai fazer OCR. Extrai o texto. Torna tudo pesquisável.\nMas vai além: páginas de manga e livros recebem o tratamento completo. O OCR extrai regiões de texto com bounding boxes. O psyche-translate traduz cada região. Uma passagem de renderização reconstrói a imagem — o texto traduzido é diagramado na região original, o texto original é removido por inpainting, e você tem uma página que parece o original mas lê no seu idioma.\nModelos de OCR diferentes pra domínios diferentes. Painéis de manga e balões de fala não são o mesmo problema que uma página de livro digitalizada. Tratar os dois como o mesmo problema é como você consegue resultado ruim.\nIsso não é hack de fim de semana. É um pipeline que vou construir direito, uma crate de cada vez.\nAs Integrações Sonarr. Radarr. Prowlarr. Lidarr. Readarr. Jackett. Discord rich presence. Bot do Discord. Webhooks. AniList sync. Import de playlist do Spotify. Streaming de torrent com priorização sequencial de peças. Tudo isso.\nMas — e isso é importante — tudo é opt-in. Cada integração é desabilitada por padrão. Você ativa o que quer. E pra cada integração remota, tem uma alternativa local. A stack arr já é local. O Jackett já é local. Pro resto: dump offline do Kitsu, Ollama local, NFO sidecar files.\nO caminho do usuário paranoico é totalmente suportado. Não como pensamento posterior. Como requisito de design.\nPlugins e a Wired É aqui que a metáfora do SEL passa de estética pra estrutural.\nO sistema de plugins é sandboxed. Terceiros podem adicionar providers de metadados, estratégias de scanner, backends de tradução, formatos de exportação — sem fazer fork, sem tocar no core. A fronteira do plugin é explícita e aplicada. O que roda dentro do plugin não alcança fora sem passar por uma interface definida. Não é só boa arquitetura. É a única arquitetura que aceito pra um servidor que guarda seus dados criptografados.\nE mais além — tem a Wired.\nNo SEL, a Wired é a rede que conecta toda consciência. O lugar onde a fronteira entre o eu e o outro se dissolve — mas só se você escolher entrar. Essa é a camada P2P opt-in da comunidade. Feeds de atividade. Notas. Listas compartilhadas. Descentralizada — nenhum servidor central controla nada. Você ativa aceitando explicitamente os termos. Estado padrão: totalmente desligado. Nenhum dado sai da sua máquina a não ser que você diga.\nHonestamente ainda não sei exatamente como construir a Wired. Sistemas distribuídos nesse nível estão fora da minha expertise atual — e esse é um dos motivos pelos quais estou escrevendo esse post. Mas sei como ela precisa parecer: você é sempre a Lain, conectando nos seus próprios termos. A rede não te consome. Você alcança ela e puxa o que quer.\nO modelo de monetização é simbólico e opcional. Loja de recompensas cosméticas — ícones, badges, packs de emoji, cargos no Discord. Estilo Discord: você paga por identidade, nunca por feature. A receita vai pra me manter focado em construir isso em vez de procurar outra forma de pagar o aluguel.\nApp Android. App desktop via Tauri. Web primeiro, apps nativos quando a experiência web estiver estável.\nPor Que GPL v3 Escolhi a GNU General Public License versão 3 de propósito.\nA GPL v3 é a licença de copyleft mais forte disponível. Se alguém fizer fork do Psyche e distribuir — comercialmente ou não — tem que liberar o código-fonte completo nos mesmos termos. Você não pega o Psyche, fecha o código e vende. Não tem caminho de exceção enterprise. Não tem truque de \u0026ldquo;dual licensing\u0026rdquo;. A comunidade construiu, a comunidade mantém.\nJá vi projetos self-hosted demais serem acqui-contratados ou lentamente enshittificados. O Jellyfin existe porque o Emby fechou o código. Isso não vai acontecer com o Psyche. A licença torna isso estruturalmente impossível.\nO Estado Atual Tô na Fase 0. O workspace Cargo está bootstrapped. O template do frontend React existe (tá bonito, por sinal). Os docs estão escritos: visão, arquitetura, modelo de segurança, contrato de criptografia.\nA Fase 1 é a próxima: sistema de auth, scanner de sistema de arquivos, API de anime, streaming, servindo o frontend como binário único. Cada fatia é testada antes da próxima começar. TDD, sem exceção. Um cume de cada vez.\nIsso vai levar anos pra atingir a visão completa. Sei disso. Não vou fingir o contrário.\nMas a fundação tá sendo construída direito. As decisões de arquitetura estão documentadas. O modelo de segurança foi escrito antes da primeira linha de código de produção. O contrato de criptografia especifica exatamente o que é criptografado, o que não é, e por quê.\nÉ assim que você constrói algo que dura.\nVocê Leu Até Aqui. Isso Significa Algo. Sério. A maioria saiu no primeiro parágrafo. Você não.\nIsso me diz que você é o tipo de pessoa que realmente pensa sobre essas coisas. Que percebe quando algo tá errado e fica se perguntando se podia ser melhor. Que ficou um pouco incomodada com a ideia de um arquivo plaintext no disco catalogando tudo que você já assistiu. Que olhou pra fragmentação do media self-hosted e pensou \u0026ldquo;alguém deveria consertar isso.\u0026rdquo;\nEssa pessoa é exatamente quem estou procurando. E digo isso no sentido mais amplo possível.\nSou técnico. Consigo escrever Rust, projetar schema de banco, conectar API. Tenho experiência com machine learning, desenvolvimento mobile e sistemas P2P. Mas tenho pontos cegos reais — e esse projeto precisa de mais do que quem sabe programar.\nNão tenho experiência nenhuma em construir comunidades. Zero. Não sei fazer servidor do Discord crescer, não sei escrever anúncio que faz as pessoas se sentirem parte de algo, não sei criar cultura em torno de um projeto. Se você já fez isso — em qualquer projeto, qualquer comunidade — esse conhecimento vale mais pro Psyche agora do que mais um PR em Rust.\nNão tenho experiência com UI/UX além do ok. O template do frontend ficou limpo, mas não sou designer full time. Não sei como um leitor deve se sentir virando página de manga. Não sei como é a experiência perfeita de player de música. Se você se importa com isso — se você já redesenhou algo e fez parecer certo — tem uma tela em branco aqui com seu nome.\nNão tenho expertise profunda em criptografia avançada. Conheço o suficiente pra projetar o pipeline AES-256-GCM, escolher Argon2id e escrever um modelo de ameaça. Mas quem consegue olhar meu esquema de derivação de chave e achar a falha sutil que eu não vi — essas pessoas ainda não estão na minha rede.\nNão tenho experiência com pentest avançado. Consigo escrever código seguro e pensar em modelos de ameaça. Mas quem ataca sistemas profissionalmente, quem encontra os edge cases que eu nem sabia que existiam, quem consegue me dizer exatamente onde minhas suposições de segurança quebram — preciso dessas pessoas.\nNão falo nada além de inglês e português. Isso vai precisar falar todos os idiomas eventualmente — não só no frontend, mas na documentação, na comunidade e no suporte. Se você fala outro idioma e se importa com esse projeto, tem espaço pra você aqui.\nNão tenho equipe. Não tenho financiamento. Não tenho empresa por trás disso. Tenho uma visão, um compilador e uma licença GPL v3 que garante que isso vai ficar livre pra sempre.\nO que estou pedindo é simples: se algo nesse post te fez pensar \u0026ldquo;eu poderia ajudar com isso\u0026rdquo; — entra em contato.\nNão só devs. Designers, escritores, construtores de comunidade, pesquisadores de ML, devs mobile, pesquisadores de segurança, pessoas com opiniões fortes sobre o que deveria ser uma boa experiência de biblioteca de mídia. Tradutores — isso vai precisar falar todos os idiomas eventualmente. Quem só quer reportar bugs com bons screenshots e logs. Quem quer escrever documentação porque sabe como é se sentir perdido num projeto sem nenhuma.\nO guia de contribuição é rigoroso com qualidade de código porque segurança é o core. Mas não tem barra pra se importar. Se você se importa, tem espaço pra você aqui.\nA codebase tem um arquivo AGENTS.md que explica como trabalhamos — foi escrito pra ser lido por humanos e agentes de IA igualmente, porque uso ferramentas de IA e espero que contribuidores também usem. A regra não é \u0026ldquo;sem IA.\u0026rdquo; A regra é \u0026ldquo;você é dono de cada linha.\u0026rdquo; Lê, entende e traz seu eu completo — ferramentas incluídas.\nManda mensagem pra enrell@proton.me. Me conta o que você faz, no que gostaria de trabalhar, ou só que leu isso e ressoou. Isso já é suficiente pra começar uma conversa.\nOu abre uma issue. Ou dá uma estrela no repositório e aparece quando algo te mover. Não precisa de compromisso pra se importar com algo.\nO sonho é ambicioso. A montanha é alta. Mas tô olhando pra esses nomes de arquivo de anime bagunçados na minha pasta local há anos, esperando a ferramenta certa existir.\nCansei de esperar.\nNos vemos na Wired.\n","permalink":"https://enrell.github.io/pt/posts/psyche/","summary":"\u003cp\u003eEram 2 da manhã. Eu tava fuçando na minha pasta de anime tentando entender por que o media server não tinha pego os metadados de um episódio novo. Abri o banco de dados SQLite por curiosidade — só pra ver o que tinha lá dentro.\u003c/p\u003e\n\u003cp\u003eDava pra ler. Tudo. Cada título. Cada nota. Cada entrada do meu histórico. Texto puro, sem criptografia nenhuma, sentado ali no meu disco.\u003c/p\u003e\n\u003cp\u003eFechei o terminal e fiquei olhando pro teto.\u003c/p\u003e","title":"Psyche: O Media Server Que Mantém Seus Dados Seus"},{"content":"Era uma tarde de sábado. Apertei o botão de ligar do meu PC e\u0026hellip; nada. As ventoinhas giraram por um segundo, os LEDs piscaram e ele morreu. Tentou de novo. Um segundo, morto. Um segundo, morto. Um loop infinito de boot sem saída de vídeo, sem códigos de erro, nada.\nO que se seguiu foi uma odisseia de uma semana com diagnósticos, palpites errados, uma placa-mãe nova, uma BIOS brickada e — quando todos os computadores da minha casa estavam mortos — uma sessão de código às 23h no meu celular para patchear uma ferramenta open-source que nunca foi feita para rodar no Android.\nEu não tinha multímetro, nem placa POST, nem visor de diagnóstico. Apenas os meus olhos e muita determinação.\nO Diagnóstico Meu primeiro suspeito foi o processador. O LED da Ethernet apagado geralmente significa que não há POST, e sem POST, o processador provavelmente não está inicializando. Minha placa era uma X99 P4 — o modelo de X99 mais básico que se pode encontrar. Com um Xeon E5-2670v3, ela vinha funcionando bem por meses.\nMas \u0026ldquo;funcionar bem por meses\u0026rdquo; em uma placa X99 barata com um Xeon de 12 núcleos é pedir demais do VRM (Módulo Regulador de Tensão).\nMeu segundo suspeito foi a fonte. Se ela não entrega corrente suficiente na linha do processador, a placa liga, mas nunca chega ao POST. As ventoinhas giram, os LEDs acendem, mas o processador \u0026ldquo;passa fome\u0026rdquo;.\nMeu terceiro suspeito foi a própria placa-mãe. Placas X99 de baixo custo têm projetos de VRM modestos. Um chip Haswell-EP de 12 núcleos puxando 120W de TDP por VRMs de 4 fases? Era apenas uma questão de quando, não de se.\nA resposta acabou sendo dois dos três: tanto a fonte quanto a placa-mãe estavam mortas. O VRM desistiu e a fonte já não estava entregando uma corrente limpa.\nHardware Novo, Esperança Nova Comprei peças de reposição:\nFonte: Husky Sledger — sólida, confiável e com folga suficiente. Placa-mãe: MR9A-H — um degrau acima da P4. Processador: Xeon E5-2640v3 — por precaução, caso o antigo também estivesse danificado. Montei tudo, apertei o power e\u0026hellip; deu boot. Tela da BIOS apareceu. O Memtest passou. Instalei o SO e comecei a usar.\nPor cerca de 30 minutos.\nO Incidente do Turbo Boost Foi aqui que cometi o meu primeiro erro.\nO E5-2640v3 tem um clock base de 2.6 GHz e turbo de até 3.4 GHz. Mas nessas placas X99 chinesas, o turbo boost geralmente vem bloqueado por padrão. Em jogos, especialmente com a minha RX 6600, esse clock extra faz diferença na estabilidade da taxa de quadros.\nPensei: \u0026ldquo;Vou gravar uma BIOS modificada com turbo unlock. Fácil. Já fiz isso antes.\u0026rdquo;\nEu tinha um backup da BIOS antiga — a da X99 P4 morta. No calor do momento, peguei o arquivo errado. Em vez de gravar a BIOS da MR9A-H, gravei a BIOS da P4 na placa MR9A-H.\nA gravação terminou. Reiniciei.\nMorto. De novo. O mesmo loop de 1 segundo.\nEu tinha acabado de inutilizar minha segunda placa-mãe em uma semana.\nO CH341A Chega A única forma de recuperar uma placa com BIOS brickada é regravando o chip externamente. Comprei um programador CH341A — o gravador SPI barato e onipresente que todo entusiasta de hardware acaba tendo um dia. Ele chegou no dia seguinte.\nMas havia um detalhe: eu não tinha nenhum computador para usá-lo.\nMeu PC estava morto. O notebook da minha mãe tinha literalmente morrido naquela semana — a tela apagou no meio de uma série e nunca mais voltou. Não havia nenhum outro computador por perto. Era noite. Nenhum amigo com desktop disponível. Nenhuma assistência técnica local aberta que soubesse lidar com gravação SPI.\nOlhei para o meu celular. Um Android moderno. USB-C. Suporte OTG.\n\u0026ldquo;Espera. O CH341A é USB. Meu celular tem USB. Será que dá para usar o celular?\u0026rdquo;\nO Problema do Android O Flashrom é a ferramenta open-source padrão para ler e escrever chips flash. Ele suporta o CH341A nativamente. O problema: é uma ferramenta voltada para o Linux de desktop. Não existe um app Android para isso.\nMeu primeiro instinto foi usar o Termux, um emulador de terminal Linux para Android. Eu poderia compilar o flashrom a partir do código-fonte.\n1 2 3 pkg install git meson ninja libusb git clone https://review.coreboot.org/flashrom.git cd flashrom Compilei. Funcionou. Mas quando tentei rodar:\n$ ./build/flashrom -p ch341a_spi -V Initializing ch341a_spi programmer libusb: error [sysfs_get_device_list] opendir devices failed, errno=13 Couldn\u0026#39;t initialize libusb! Permissão negada. No Android, o SELinux bloqueia o acesso direto ao diretório /dev/bus/usb/. Sem root, o libusb_init() falha porque tenta escanear o barramento USB na inicialização, e esse scan é negado.\nA única forma de acessar hardware USB no Android sem root é através do termux-usb, que solicita permissão ao sistema e entrega um File Descriptor (FD) já aberto. Mas o flashrom não sabe o que fazer com um FD; ele espera escanear o barramento por conta própria.\nEu tinha duas opções: esperar por um computador de verdade ou fazer o flashrom entender o Android.\nEscolhi a segunda.\nAdicionando Suporte a FD do Android no Flashrom A ideia principal foi esta: o flashrom usa a libusb para se comunicar com o CH341A. O fluxo normal é:\nlibusb_init(NULL) — inicializa a biblioteca (escaneia /dev/bus/usb/). libusb_open_device_with_vid_pid() — encontra e abre o CH341A pelos IDs USB. Comunica-se com o dispositivo. O passo 1 falha no Android porque o SELinux bloqueia o escaneamento do barramento USB. O passo 2 falha porque depende do primeiro.\nMas a libusb possui uma função pouco conhecida: libusb_wrap_sys_device(). Ela recebe um descritor de arquivo (FD) já aberto e o encapsula diretamente em um libusb_device_handle. Sem scans, sem buscas por VID/PID. Apenas: \u0026ldquo;aqui está o FD, me dê o handle\u0026rdquo;.\nO detalhe? Você ainda precisa de um libusb_context válido. E o libusb_init() tenta escanear dispositivos. A solução é a opção LIBUSB_OPTION_NO_DEVICE_DISCOVERY, introduzida na libusb 1.0.24. Ela diz à biblioteca: \u0026ldquo;inicialize-se, mas não saia procurando no /dev/bus/usb/\u0026rdquo;.\nAqui está o que adicionei ao programmers/ch341a_spi.c:\n1 2 3 4 5 6 7 8 9 10 11 12 13 /* Android FD support: get FD from environment variable */ static int get_android_usb_fd(void) { const char *env_fd = getenv(\u0026#34;ANDROID_USB_FD\u0026#34;); if (env_fd) { int fd = atoi(env_fd); if (fd \u0026gt; 0) { msg_pdbg(\u0026#34;Using Android USB FD from env: %d\\n\u0026#34;, fd); return fd; } } return -1; } E na função de inicialização, antes de qualquer escaneamento USB:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 static int ch341a_spi_init(const struct programmer_cfg *cfg) { int android_fd = -1; char *fd_param = extract_programmer_param_str(cfg, \u0026#34;fd\u0026#34;); if (fd_param) { android_fd = atoi(fd_param); free(fd_param); } if (android_fd \u0026lt;= 0) android_fd = get_android_usb_fd(); libusb_context *ctx = NULL; if (android_fd \u0026gt; 0) { /* Android FD mode: pular scan de dispositivos */ struct libusb_init_option opts = { .option = LIBUSB_OPTION_NO_DEVICE_DISCOVERY }; ret = libusb_init_context(\u0026amp;ctx, \u0026amp;opts, 1); if (ret \u0026lt; 0) { msg_perr(\u0026#34;Couldn\u0026#39;t initialize libusb context: %s\\n\u0026#34;, libusb_error_name(ret)); return -1; } /* Encapsular o FD diretamente em um device handle */ ret = libusb_wrap_sys_device(ctx, (intptr_t)android_fd, \u0026amp;data-\u0026gt;handle); if (ret \u0026lt; 0) { msg_perr(\u0026#34;Failed to wrap USB device fd %d: %s\\n\u0026#34;, android_fd, libusb_error_name(ret)); libusb_exit(ctx); return -1; } msg_pdbg(\u0026#34;Successfully wrapped USB device from FD %d\\n\u0026#34;, android_fd); } else { /* Caminho original: scan por VID/PID (precisa de root no Android) */ ret = libusb_init(\u0026amp;ctx); // ... código existente ... } // ... resto da inicialização ... } O patch completo possui cerca de 130 linhas. Não é muito código, mas muda completamente como o flashrom inicializa no Android.\nTestando ao Vivo Compilei o flashrom com o patch:\n1 2 meson setup build -Dtests=disabled -Dwerror=false -Dprogrammer=ch341a_spi ninja -C build Em seguida, escrevi um pequeno wrapper em C, já que o termux-usb passa o FD como argumento para o processo filho:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main(int argc, char **argv) { if (argc \u0026lt; 2) { fprintf(stderr, \u0026#34;Uso: %s \u0026lt;fd\u0026gt;\\n\u0026#34;, argv[0]); return 1; } int fd = atoi(argv[1]); setenv(\u0026#34;ANDROID_USB_FD\u0026#34;, argv[1], 1); execl(\u0026#34;./build/flashrom\u0026#34;, \u0026#34;flashrom\u0026#34;, \u0026#34;-p\u0026#34;, \u0026#34;ch341a_spi\u0026#34;, \u0026#34;--flash-name\u0026#34;, NULL); return 1; } E então:\n1 termux-usb -r -e ./detect_bios /dev/bus/usb/001/003 A saída:\nInitializing ch341a_spi programmer Using Android USB FD from parameter: 7 LibUSB initialized with NO_DEVICE_DISCOVERY for FD mode Successfully wrapped USB device from FD 7 Probing for Generic unknown SPI chip (REMS), 0 kB: compare_id: id1 0xa1, id2 0x2818 Found Unknown flash chip \u0026#34;SFDP-capable chip\u0026#34; (16384 kB, SPI). Funcionou. O CH341A estava se comunicando com o chip da BIOS. Pelo meu celular. Via USB. Sem root.\nA Recuperação Com a conexão estabelecida, fiz primeiro um backup completo:\n1 termux-usb -r -e ./flash_backup /dev/bus/usb/001/003 O backup do chip de 16MB levou cerca de 2 minutos. Depois verifiquei — o hash foi idêntico entre duas leituras, confirmando uma conexão estável.\nEm seguida, gravei a BIOS correta — o arquivo real da MR9A-H que eu tinha salvo no Google Drive:\n1 2 termux-usb -r -E -e \u0026#39;./build/flashrom -p ch341a_spi \\ -w /storage/emulated/0/Download/backup_chip_full.rom -V\u0026#39; A gravação levou cerca de 6 minutos. O flashrom reportou:\nFound Unknown flash chip \u0026#34;SFDP-capable chip\u0026#34; (16384 kB, SPI). Erasing and writing flash chip... VERIFYING VERIFIED. Removi o clipe do CH341A da placa-mãe. Reconectei a energia. Apertei o power.\nDeu boot.\nTela da BIOS. Memória detectada. Processador funcionando. A placa-mãe estava viva de novo.\nValidei a gravação lendo o chip mais uma vez e comparando o hash com o arquivo original:\nArquivo original: 5fc52519de9b9b9f41e0e62810307d09 Lido do chip: 5fc52519de9b9b9f41e0e62810307d09 ✅ CONFERE O Que Eu Aprendi 1. Hardware de baixo custo tem limites Uma placa X99 de 30 dólares rodando um Xeon de 12 núcleos não é uma combinação sustentável. O VRM vai acabar cedendo. Se você utiliza processadores de alto desempenho, invista em uma placa capaz de fornecer a energia necessária.\n2. Sempre confira os arquivos de firmware três vezes Eu tinha o backup correto, mas acabei usando o arquivo errado. Uma pequena distração no nome do arquivo me custou um dia inteiro e a compra de um CH341A. Quando for gravar um firmware, leia o nome do arquivo em voz alta antes de apertar Enter.\n3. O Android é Linux, mas não é aquele Linux O SELinux está lá por um motivo, mas torna o acesso ao hardware um processo penoso sem root. O caminho do libusb_wrap_sys_device() é a forma correta de lidar com USB no Android — e é pouquíssimo documentado.\n4. O ecossistema de código aberto é incrível O Flashrom é mantido por desenvolvedores do coreboot que se importam profundamente com a liberdade do hardware. O fato de eu poder modificá-lo, compilá-lo em um celular e recuperar uma placa-mãe é o poder do open-source em ação.\n5. Celulares são computadores de verdade Fiz uma recuperação completa de firmware de placa-mãe usando apenas um celular. Sem notebook, sem desktop. Apenas um celular, um programador CH341A de poucos reais e um pouco de código C. O futuro chegou.\nO Patch O patch de suporte a FD do Android para o flashrom está disponível na minha cópia de trabalho. Se houver interesse, vou limpá-lo e enviá-lo para o Gerrit do flashrom.\nO uso é simples:\n1 2 3 4 5 # Com wrapper termux-usb -r -e ./meu_wrapper /dev/bus/usb/001/XXX -- -V # Ou com variável de ambiente ANDROID_USB_FD=7 ./build/flashrom -p ch341a_spi -c FM25W128 -w firmware.bin Minha X99 está viva. Meu celular provou seu valor como ferramenta de recuperação. E o flashrom agora tem um suporte a Android que não existia antes deste fim de semana.\nÀs vezes, o melhor código nasce do desespero às 23h, sem computador e com uma placa-mãe morta encarando você.\nSe você está trabalhando na recuperação de hardware via Android ou tem perguntas sobre o patch, deixe um comentário abaixo. E se este post te ajudou, compartilhe — alguém por aí pode estar com uma placa inutilizada agora mesmo tendo apenas um celular no bolso.\nSee you in the Wired\n","permalink":"https://enrell.github.io/pt/posts/flashrom-android-fd/","summary":"\u003cp\u003eEra uma tarde de sábado. Apertei o botão de ligar do meu PC e\u0026hellip; nada. As ventoinhas giraram por um segundo, os LEDs piscaram e ele morreu. Tentou de novo. Um segundo, morto. Um segundo, morto. Um loop infinito de boot sem saída de vídeo, sem códigos de erro, nada.\u003c/p\u003e\n\u003cp\u003eO que se seguiu foi uma odisseia de uma semana com diagnósticos, palpites errados, uma placa-mãe nova, uma BIOS brickada e — quando todos os computadores da minha casa estavam mortos — uma sessão de código às 23h no meu celular para patchear uma ferramenta open-source que nunca foi feita para rodar no Android.\u003c/p\u003e","title":"Eu Patchei o Flashrom no Celular Para Recuperar uma Placa-Mãe Morta"},{"content":"Eram 19h de uma quarta-feira. Eu estava olhando para o meu terminal, vendo o OpenCode tentar responder a uma pergunta sobre uma biblioteca que ele nunca tinha visto antes.\nO LLM estava fazendo o seu melhor. Mas estava alucinando endpoints de API que não existiam.\nE eu pensei: \u0026ldquo;Por que minha IA não pode simplesmente\u0026hellip; pesquisar na web?\u0026rdquo;\nO Problema Eu uso o OpenCode, o Claude Code e às vezes o Crush como meus companheiros diários de programação. É poderoso. Mas tem um ponto cego: a busca nativa na web não consegue acessar sites protegidos pela Cloudflare.\nIsso significa:\nMuitas falhas e tokens desperdiçados. Sites com alta proteção anti-bot? Inacessíveis. Notícias atuais de grandes fontes? Desconhecidas. Eu precisava de algo que permitisse ao meu LLM pesquisar na web e buscar conteúdo sob demanda. Algo leve. Algo que eu controlasse.\nA Ideia searxng-web-fetch-mcp Eu queria um servidor MCP (Model Context Protocol) que fizesse duas coisas:\nBuscar na web — Usando minha instância local do SearXNG Buscar conteúdo — Extrair texto limpo de qualquer URL É isso. Sem bloat (inchaço). Sem vendor lock-in. Apenas busca e extração.\nPor que Crystal? Eu escolhi Crystal por três motivos:\nVelocidade — Crystal compila para código nativo. Rápido. Extremamente rápido. Ergonomia — Sintaxe semelhante a Ruby, que é linda de ler e escrever. Você pode construir um aplicativo completo em apenas algumas linhas de código. Manutenibilidade — A tipagem forte captura bugs em tempo de compilação. A base de código permanece limpa e fácil de manter. Um binário de 12MB que inicia em milissegundos? Esse é o ponto forte do Crystal.\nCuriosidade: Comecei este projeto no dia 25 de março às 19h. À meia-noite, o núcleo estava funcionando. É assim que é rápido desenvolver em Crystal.\nA Stack Componente Propósito Crystal Servidor principal, desempenho Lexbor Parsing de HTML MCP Protocol Integração com assistente de IA SearXNG Busca descentralizada Byparr Proxy anti-captcha para extração Estatísticas do Código ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Language Files Lines Code Comments Blanks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Crystal 9 815 689 6 120 Shell 4 414 348 4 62 YAML 1 26 20 0 6 ───────────────────────────────────────────────────────────────────────────────── Markdown 2 241 0 160 81 |- BASH 2 36 16 11 9 |- Crystal 1 13 10 2 1 |- Dockerfile 1 18 17 0 1 |- JSON 1 30 30 0 0 |- YAML 1 82 72 0 10 (Total) 420 145 173 102 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Total 16 1675 1202 183 290 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 689 linhas de Crystal. Nada mal para um servidor MCP completo, com extração de conteúdo e busca em lote (Batch Fetching).\nComo Funciona Aqui está o fluxo básico:\nLLM → MCP → searxng-web-fetch-mcp ↓ searxng_web_search() ↓ web_fetch() ↓ Markdown Limpo → De volta ao LLM Simples. Elegante. Rápido.\nFerramenta 1: searxng_web_search 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class SearxngWebSearch \u0026lt; MCP::AbstractTool @@tool_name = \u0026#34;searxng_web_search\u0026#34; @@tool_description = \u0026#34;Search the web using SearXNG\u0026#34; def invoke(params) query = params[\u0026#34;query\u0026#34;].as_s num_results = params[\u0026#34;num_results\u0026#34;]?.try(\u0026amp;.as_i) || 10 # Call SearXNG API response = HTTP::Client.get(\u0026#34;#{SEARXNG_URL}/search\u0026#34;, headers: HTTP::Headers{\u0026#34;Accept\u0026#34; =\u0026gt; \u0026#34;application/json\u0026#34;}, query: URI::Params.encode({\u0026#34;q\u0026#34; =\u0026gt; query, \u0026#34;format\u0026#34; =\u0026gt; \u0026#34;json\u0026#34;}) ) parse_results(response.body) end end Ferramenta 2: web_fetch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class WebFetch \u0026lt; MCP::AbstractTool @@tool_name = \u0026#34;web_fetch\u0026#34; @@tool_description = \u0026#34;Fetch and extract content from a URL\u0026#34; def invoke(params) url = params[\u0026#34;url\u0026#34;].as_s # Fetch through anti-captcha proxy html = HTTP::Client.get(url) # Extract main content extractor = TrafilaturaExtractor.new result = extractor.extract(html.body) # Convert to clean Markdown markdown = HtmlToMarkdown.convert(result.content) { success: true, text: markdown, metadata: result.metadata } end end O Algoritmo de Extração A parte mais difícil foi a extração de conteúdo. Sites são uma bagunça. Barras laterais, anúncios, menus de navegação — tudo ruído.\nEu portei a lógica principal do go-trafilatura, que usa heurísticas inteligentes:\nRemover ruído — Scripts, estilos, navegação, rodapé, anúncios Pontuar elementos — Com base na densidade de texto, densidade de links Impulsionar padrões — Nomes de classes como \u0026ldquo;content\u0026rdquo;, \u0026ldquo;article\u0026rdquo;, \u0026ldquo;main\u0026rdquo; Penalizar padrões — Nomes de classes como \u0026ldquo;comment\u0026rdquo;, \u0026ldquo;sidebar\u0026rdquo;, \u0026ldquo;footer\u0026rdquo; Extrair metadados — Título, autor, data, idioma das meta tags Funciona surpreendentemente bem para a maioria dos artigos.\nSuporte Multiplataforma Porque não? O script de instalação detecta sua plataforma automaticamente:\n1 curl -sL [https://raw.githubusercontent.com/enrell/searxng-web-fetch-mcp/main/install.sh](https://raw.githubusercontent.com/enrell/searxng-web-fetch-mcp/main/install.sh) | bash Plataformas suportadas:\nLinux: x86_64, arm64, riscv64 macOS: x86_64, arm64 (Apple Silicon) Windows: x86_64 Um comando. O binário cai em ~/.local/bin. Feito.\nConfiguração 1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;mcp\u0026#34;: { \u0026#34;searxng-web\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;local\u0026#34;, \u0026#34;command\u0026#34;: [\u0026#34;~/.local/bin/searxng-web-fetch-mcp\u0026#34;], \u0026#34;environment\u0026#34;: { \u0026#34;SEARXNG_URL\u0026#34;: \u0026#34;http://localhost:8888\u0026#34;, \u0026#34;BYPARR_URL\u0026#34;: \u0026#34;http://localhost:8191\u0026#34; } } } } Adicione à sua configuração do OpenCode. Reinicie. A IA agora pode pesquisar e extrair.\nO Que Aprendi Este projeto me ensinou várias coisas:\n1. Variáveis de ambiente são complicadas O ENV.fetch do Crystal é avaliado em tempo de compilação. Passar variáveis de ambiente para processos filhos? Surpreendentemente cheio de nuances. Passei horas depurando por que o npx não estava recebendo minhas variáveis.\n2. Fazer linting cedo economiza tempo Rodar o Ameba (linter do Crystal) em cada commit pegou 14 problemas de uma vez só. Nomes de parâmetros de bloco, espaços em branco no final, formatação — tudo corrigido antes que se tornassem problemas.\n3. Lançamentos multiplataforma são essenciais Usuários em diferentes sistemas operacionais e arquiteturas precisam de binários pré-compilados. GitHub Actions + linkagem estática do Crystal = mágica.\n4. Mantenha as coisas mínimas Duas ferramentas. Sem banco de dados. Sem autenticação. Sem complexidade. Apenas busca e extração. É por isso que funciona.\nExemplo no Mundo Real Depois de construir isso, pedi ao OpenCode para pesquisar sobre uma biblioteca:\n\u0026gt; Pesquise a documentação mais recente do \u0026#34;crystal-pg\u0026#34; \u0026gt; Busque o README do repositório no GitHub \u0026gt; Mostre-me como me conectar ao PostgreSQL E ele fez. Porque tinha informações reais. Não palpites alucinados.\nO Que Vem a Seguir 🤖 Melhor extração de conteúdo — Lidar com mais formatos de sites 📊 Cache de respostas — Armazenar resultados de busca/extração em cache para consultas repetidas 🔍 Agregação de motores de busca — Suportar mais motores de busca 📦 Docker compose — Implantação de todos os serviços em um clique Atualização: Suporte a Concorrência (v0.2.1) Acabei de lançar a v0.2.1 com concorrência baseada em fibers! O protocolo MCP processa requisições sequencialmente, mas adicionei a busca em lote de URLs (batch fetching), que processa múltiplas URLs em paralelo dentro de uma única requisição.\nComo Funciona O spawn do Crystal cria fibers leves. Combinado com um canal de semáforo, isso limita as operações concorrentes de E/S (I/O):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 module Utils module ConcurrentHTTP def self.run_parallel(max_concurrent : Int32, tasks : Array(Proc(T))) : Array(T) forall T semaphore = Channel(Nil).new(max_concurrent) channels = Array(Channel(T | Exception)).new(tasks.size) tasks.each do |task| channel = Channel(T | Exception).new spawn do semaphore.send(nil) begin result = task.call channel.send(result) rescue ex channel.send(ex) ensure semaphore.receive end end end # Collect results... end end end Desempenho Modo Taxa de Transferência (Throughput) Busca sequencial ~2 req/s Lote concorrente (10 URLs) 33 URLs/s Lote concorrente (30 URLs) 25 URLs/s Isso é ~10-15x mais rápido que o processamento sequencial!\nUso 1 2 3 4 5 6 7 { \u0026#34;urls\u0026#34;: [ \u0026#34;[https://example.com/article1](https://example.com/article1)\u0026#34;, \u0026#34;[https://example.com/article2](https://example.com/article2)\u0026#34;, \u0026#34;[https://example.com/article3](https://example.com/article3)\u0026#34; ] } Configure o limite de concorrência com MAX_CONCURRENT_REQUESTS (padrão: 30).\nA principal sacada: os fibers do Crystal são leves (KB cada) em comparação com as threads do SO (MB cada), tornando-os perfeitos para cargas de trabalho limitadas por E/S (I/O-bound), como requisições HTTP.\nExperimente 1 2 3 4 5 # Instalar curl -sL [https://raw.githubusercontent.com/enrell/searxng-web-fetch-mcp/main/install.sh](https://raw.githubusercontent.com/enrell/searxng-web-fetch-mcp/main/install.sh) | bash # Configurar (adicione à sua configuração do MCP) # Em seguida, use com OpenCode, Claude Code ou qualquer cliente compatível com MCP O repositório no GitHub está aberto. Issues e PRs são bem-vindos.\nE você? Existe alguma capacidade que está faltando no seu assistente de IA? Deixe-me saber nos comentários.\nNos vemos na Wired\n","permalink":"https://enrell.github.io/pt/posts/searxng-web-fetch-mcp/","summary":"\u003cp\u003eEram 19h de uma quarta-feira. Eu estava olhando para o meu terminal, vendo o OpenCode tentar responder a uma pergunta sobre uma biblioteca que ele nunca tinha visto antes.\u003c/p\u003e\n\u003cp\u003eO LLM estava fazendo o seu melhor. Mas estava alucinando \u003cem\u003eendpoints\u003c/em\u003e de API que não existiam.\u003c/p\u003e\n\u003cp\u003eE eu pensei: \u003cem\u003e\u0026ldquo;Por que minha IA não pode simplesmente\u0026hellip; pesquisar na web?\u0026rdquo;\u003c/em\u003e\u003c/p\u003e\n\u003ch2 id=\"o-problema\"\u003eO Problema\u003c/h2\u003e\n\u003cp\u003eEu uso o OpenCode, o Claude Code e às vezes o Crush como meus companheiros diários de programação. É poderoso. Mas tem um ponto cego: a busca nativa na web não consegue acessar sites protegidos pela Cloudflare.\u003c/p\u003e","title":"Construindo um MCP em Crystal para Busca na Web e Extração de Conteúdo"},{"content":"Recentemente, assisti a um vídeo do canal Veritasium chamado \u0026ldquo;The Internet Was Weeks Away From Disaster and No One Knew\u0026rdquo; (A Internet Estava a Semanas do Desastre e Ninguém Sabia). O vídeo detalha a história do \u0026ldquo;backdoor\u0026rdquo; no XZ Utils — um ataque de engenharia social incrivelmente inteligente que durou anos e quase comprometeu o OpenSSH, junto com grande parte da comunidade de código aberto. O invasor passou anos conquistando confiança e começou a inserir código malicioso, aos poucos, dentro de uma biblioteca de compressão super específica que é base para muitas outras ferramentas fundamentais.\nAssistindo a esse vídeo, a ideia para este projeto surgiu. Passamos bem perto de uma catástrofe global nas redes, e isso me fez refletir.\nNo fundo, quantos de nós realmente entendemos o conteúdo que estamos rodando? Temos a rotina de rodar npm install, cargo build ou bun install, e só assistimos ao terminal enlouquecer com downloads. O node_modules – e pastas do tipo – acabaram virando caixas-pretas completas para nós. O equivalente a você conectar sua cabeça diretamente in \u0026ldquo;The Wired\u0026rdquo; e não checar nenhuma de suas rotas. Se uma única dependência vital for comprometida, basicamente não teríamos para onde fugir.\nEu não gosto de agir às cegas ou no escuro. Minha vibe trabalhando nos projetos passa por entender o ecossistema ali dentro. Gosto de ver de onde vêm os exageros, checar a estrutura geral dessas dependências e os elos frágeis – compreender de perto quem e o que eu estou puxando pra rodar na minha máquina.\nSendo assim, resolvi criar algo que lidasse com isso.\nConheça o depg (Grafo de Dependências) Meu objetivo era ter uma ferramenta absurdamente rápida e local para mapear tudo que um projeto tem de dependência. Nada daquelas doideiras de ficar exportando seu arquivo \u0026ldquo;Cargo.lock\u0026rdquo; para serviços web aleatórios de terceiros – apenas uma ferramenta CLI robusta, direta e local capaz de rastrear as árvores de código, gerando um grafo visual completo e interativo em um segundo.\nCodificar ela com Rust (Edition 2024) fluiu bem. Afinal, se formos focar em ferramentas pelo terminal (CLI), velocidade real, performance na memória das operações assíncronas e eficiência em recursos já devem ser prioridades garantidas.\nCódigo Fonte \u0026amp; Como Instalar O código-fonte completo já está aberto aqui: https://github.com/enrell/dependencies-graph\nPara macOS ou Linux:\n1 curl -fsSL https://raw.githubusercontent.com/enrell/dependencies-graph/main/install.sh | sh Para Windows (com PowerShell):\n1 irm https://raw.githubusercontent.com/enrell/dependencies-graph/main/install.ps1 | iex Via Cargo:\n1 cargo install --git https://github.com/enrell/dependencies-graph.git Como Usar 1 2 depg run depg run --depth 2 --port 8080 --open O Que Rola Por Baixo do Capô Ao chamar o depg, ele faz uma busca na sua localização por lockfiles comuns. Ao encontrar, o CLI lê completamente as árvores e garante o suporte necessário baseando-se nas abordagens ecossistêmicas adequadas. Estes dados dos grafos de dependências recebem serialização, viram uma estrutura pronta e são acessados junto a um servidor local super otimizado via web feito localmente por meio do Axum. Já do lado da aplicação em renderizar este grafo, conseguimos acessar isso quase que instantâneo em uma área responsiva visual – tudo puxado com a força orientada com suporte à física local também. Aspectos da Arquitetura Núcleo Interno: Rust em formato puro garantindo solidez, usando Clap, Anyhow \u0026amp; Serde. Sistema Integrado do Parser: Toda a estrutura aqui cresce via suporte das chamadas Traits tornando incrivelmente fácil incluir as próximas linguagens futuramente. Estruturação do Server: Usa a estrutura Axum + modelagem sob o runtime Tokio (com arquivos fixos que fecham junto dentro da compilação de binários final). No lado Visual: É a flexibilidade com Cytoscape.js rodando puramente no JS padrão sem pesar a máquina. A ideia é: um único comando, no enter – e a diversão magicamente acontece visualizando os projetos no seu navegador padrão abrindo uma estrutura de conexões orgânica focada nessas dependências do código em poucos cliques. Poder acessar detalhes, e chegar exatamente mais fundo mapeando partes distantes que conectam no fundo toda e qualquer base de operações ali no seu trabalho.\nSaber Metade de Tudo Isso que gerou o depg focou além em apenas fazer uns desenhos agradáveis. Era retornar com nosso controle visual de verdade para ver se o que colocavam com os escândalos daquelas partes envolvendo bibliotecas do backdoors do XZ na vida real podem parar nas portas desses grandes códigos fontes base abertos que nós temos plena certeza confiar antes e também se garantir que não somos completamente cegos para não acompanhar quem estamos absorvendo com tudo nas costas sem validar nada.\nQuando criamos as próprias formas ágeis de podermos observar os detalhes destas fiações visuais fundamentais – a premissa volta para tentar detectar coisas fora do padrão rapidamente um pouco antes dos processos alcançarem locais perigosos da operação de produções globais que operam essas ferramentas.\nVerifiquem com os códigos de portas sempre abertas; testem rodar na base gigante dos seus trabalhos codificados – o local com os traços das estruturas se parecem hoje com bairros projetados logicamente organizados de prédios conectados bem ajustados ali? Ou aquilo mais já parece uma tempestade sem fim e de muito caos amarrado sem direção ali?\nSee you in the The Wired.\n","permalink":"https://enrell.github.io/pt/posts/depg-a-graph-dependencies-visualizer/","summary":"\u003cp\u003eRecentemente, assisti a um vídeo do canal Veritasium chamado \u0026ldquo;The Internet Was Weeks Away From Disaster and No One Knew\u0026rdquo; (A Internet Estava a Semanas do Desastre e Ninguém Sabia). O vídeo detalha a história do \u0026ldquo;backdoor\u0026rdquo; no XZ Utils — um ataque de engenharia social incrivelmente inteligente que durou anos e quase comprometeu o OpenSSH, junto com grande parte da comunidade de código aberto. O invasor passou anos conquistando confiança e começou a inserir código malicioso, aos poucos, dentro de uma biblioteca de compressão super específica que é base para muitas outras ferramentas fundamentais.\u003c/p\u003e","title":"Estamos Voando às Cegas: Por Que Construí um Visualizador de Grafo de Dependências em Rust"},{"content":"O último post foi sobre decisões de arquitetura.\nEste aqui é sobre execução.\nPassei esse ciclo transformando ideias em algo executável e testável. Não polido. Não \u0026ldquo;magia de IA\u0026rdquo;. Apenas fundações reais.\nO que foi entregue desde o último post 1) Fatia vertical da API REST navi-agent agora tem um backbone de API funcional com rotas de health, task e agent, incluindo fluxo síncrono.\nIsso me deu um caminho completo de request → service → persistence → response, que é onde falhas reais de design começam a aparecer.\n2) Persistência SQLite para tasks e agents Movi o estado central da memória para o SQLite tanto para tasks quanto para metadados de agent.\nParece simples, mas muda tudo: comportamento ao reiniciar, debugging e confiança nos testes iterativos.\n3) Sincronização real de agents + carregamento baseado em dados A sincronização de agents não é mais falsa.\nOs agents são carregados a partir de definições no sistema de arquivos e sincronizados na persistência.\nIsso se alinha ao princípio central do navi-agent: agents são dados, não lógica hardcoded.\n4) Loop REPL/TUI para testes locais rápidos Adicionei um REPL simples para exercitar comportamentos rapidamente sem precisar passar por HTTP o tempo todo.\nO foco atual é tornar os outputs explícitos para que fique óbvio o que é:\nmensagem do usuário sinal de \u0026ldquo;pensamento\u0026rdquo; do modelo resposta de ferramenta resposta final do orquestrador Essa visibilidade importa mais do que uma UI bonita agora.\n5) Loop do orquestrador do Sprint 1 + chamada de ferramentas Implementei um loop básico de orquestrador onde o modelo pode requisitar ferramentas através de um formato estruturado de tool-call, as ferramentas executam e os resultados alimentam o próximo turno.\nSuporta padrões de chamada de ferramenta simples e múltiplos na mesma troca.\n6) Caminho básico de integração MCP Um caminho MCP mínimo está conectado para o fluxo de execução de ferramentas, suficiente para validar a direção da arquitetura sem construir demais cedo.\n7) Onboarding de dev melhorado com .env A experiência de clone limpo agora suporta configuração local baseada em .env para:\nChave de API provider padrão modelo padrão modo de ambiente (development/production) Na inicialização em modo desenvolvimento, navi-agent imprime quais arquivos .env foram carregados.\nSem adivinhações, sem \u0026ldquo;por que essa configuração não está aplicando?\u0026rdquo; confusões.\n8) Criação do diretório de configuração no primeiro uso é explícita navi-agent agora cria seu diretório de configuração de usuário no primeiro uso antes da execução dos comandos.\nIsso remove bastante atrito oculto para novos contribuidores.\nTeste de chamada de ferramentas Decisão de logging (importante) Tive uma longa discussão de arquitetura sobre telemetria e estou escolhendo o caminho pragmático:\nSem OpenTelemetry por enquanto Usar o log/slog nativo do Go Logs locais em JSON Lines (.jsonl) estruturados Rotação Tornar os logs fáceis para ferramentas MCP inspecionarem Por quê: navi-agent é local-first agora. Quero observabilidade sem inchaço de dependências ou custo de complexidade.\nIsso mantém as coisas minimais, testáveis e preparadas para o futuro.\nNota de processo: arquiteto + execução em par com IA Reescrevi esse projeto várias vezes. Protótipo → refatoração → erro → reescrever de novo.\nIsso não é mais caos. É processo intencional.\nEstou rodando um ciclo estilo XP com programação em par com IA:\nEu cuido da arquitetura, fronteiras e trade-offs. A IA acelera a implementação, iteração de testes e refatorações mecânicas. Então reescritas não são sinais de falha. São parte da convergência de design.\nReplanejamento do Sprint (atualizado) Sprint 1 — Estabilidade do runtime de agente único (atual) TUI simples do navi-agent ✅ Agente orquestrador principal básico ✅ Chamada de ferramentas básica ✅ Integração MCP básica ✅ Onboarding com .env ✅ Revisão/replanejamento dos requisitos de pasta de configuração de agent ⏳ Fundação de logging (slog + JSONL) ⏳ Objetivo: A TUI pede ao modelo para usar ferramentas e o fluxo de ferramentas é confiável/observável.\nSprint 2 — Fatia vertical de agente especialista (sem agência completa ainda) Agentes especialistas básicos: planner, researcher, coder, tester (um ativo por vez) Expansão básica de ferramentas MCP nativas Mais comandos CLI Melhorias de UX da TUI (clareza, rastreabilidade, consistência) Objetivo: A TUI pode chamar ferramentas MCP e rotear para um agente especialista por turno.\nSprint 3 — Implantação de agência Delegação multi-agent controlada Guardrails (limites, política de falha, caminhos de recuperação) Melhor visão de timeline/debug de orquestração Objetivo: coordenação multi-agent com comportamento previsível e traces debugáveis.\nInfraestrutura sobrevive a ciclos de hype.\nEntão estou construindo infraestrutura primeiro.\nSe você também está construindo ferramentas de IA local-first, adoraria seu feedback sobre a direção de logging e a estrutura dos sprints.\n","permalink":"https://enrell.github.io/pt/posts/navi-devlog-1/","summary":"\u003cp\u003eO último post foi sobre decisões de arquitetura.\u003c/p\u003e\n\u003cp\u003eEste aqui é sobre \u003cstrong\u003eexecução\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003ePassei esse ciclo transformando ideias em algo executável e testável. Não polido. Não \u0026ldquo;magia de IA\u0026rdquo;. Apenas fundações reais.\u003c/p\u003e\n\u003ch2 id=\"o-que-foi-entregue-desde-o-último-post\"\u003eO que foi entregue desde o último post\u003c/h2\u003e\n\u003ch3 id=\"1-fatia-vertical-da-api-rest\"\u003e1) Fatia vertical da API REST\u003c/h3\u003e\n\u003cp\u003enavi-agent agora tem um backbone de API funcional com rotas de health, task e agent, incluindo fluxo síncrono.\u003c/p\u003e\n\u003cp\u003eIsso me deu um caminho completo de request → service → persistence → response, que é onde falhas reais de design começam a aparecer.\u003c/p\u003e","title":"navi-agent Devlog #1 — Sprint 1 em andamento: TUI, loop do orquestrador, caminho MCP e onboarding com .env"},{"content":"Eram 3 da manhã quando tive a ideia do navi-agent, alguns meses atrás. Eu estava na cama pensando sobre o impacto dos LLMs nas hard skills dos desenvolvedores. Antes do boom dos LLMs, eu melhorava minhas habilidades de programação construindo projetos para o meu próprio uso. Mas quando a OpenAI lançou o GPT-3, vi que essa tecnologia poderia ser útil. Passei muito tempo brincando com a geração de código do GPT-3, e lembro da sensação que tive quando o usei para aprender POO (Programação Orientada a Objetos). Eu fiquei tipo: \u0026ldquo;Que p*rra é essa! Como esses caras fazem isso?\u0026rdquo;. Essa foi a faísca que fez meu hiperfoco ativar para estudar a área.\nEstudei o básico para entender as arquiteturas dos modelos e, recentemente, concluí a disciplina de Recuperação de Informação e Inteligência Artificial na faculdade. Agora tenho um conhecimento sólido para começar a entender o presente e o futuro dos LLMs.\nNeste artigo, vou apresentar as decisões arquiteturais por trás do navi-agent e alguns insights úteis sobre os pontos fortes e fracos dos agentes, com base no meu humilde conhecimento sobre LLMs.\nA diferença entre agentes e agência Olha, estou imerso na área de IA há um bom tempo e posso te dizer uma coisa: todo mundo joga a palavra \u0026ldquo;agente\u0026rdquo; para todo lado como se não houvesse amanhã. Mas a questão é a seguinte — um agente por si só? É apenas uma chamada de função chique com fobia de compromisso.\nDeixa eu explicar isso com algo real. Imagine que você tem um único modelo de IA que pode responder a perguntas. Legal, né? Isso é um agente. Ele é reativo. Você pergunta, ele responde. Você dá o prompt, ele gera o output. Não há nada de errado com isso — mas não é exatamente\u0026hellip; autônomo.\nAgora, e se essa mesma IA pudesse decidir quando pesquisar na web, escolher chamar um banco de dados e optar por salvar os resultados em algum lugar? Ainda é um agente. Mas no momento em que você conecta vários agentes, dá a eles papéis, responsabilidades e uma forma de se comunicarem?\nÉ aí que você tem uma agência.\nA Armadilha do Agente Solitário Já vi esse erro vezes demais. Desenvolvedores criam um \u0026ldquo;super agente\u0026rdquo; que tenta fazer de tudo:\n1 2 3 4 5 6 7 8 // Não faça isso func (a *Agent) HandleEverything(input string) string { // Verifica se precisa pesquisar // Verifica se precisa calcular // Verifica se precisa salvar // Verifica se precisa chamar uma API // ...você entendeu a ideia } Isso é o que eu chamo de padrão \u0026ldquo;agente deus\u0026rdquo; (god agent). Funciona para demonstrações, mas desmorona em produção. Por quê? Porque agentes únicos não têm perspectiva. Eles tentam ser tudo ao mesmo tempo.\nA Abordagem de Agência Uma agência é diferente. Pense nela como uma equipe:\n1 2 3 4 5 6 7 // Este é o caminho type Agency struct { Planner *Agent // Decide os passos (Planejador) Researcher *Agent // Pesquisa e coleta informações (Pesquisador) Coder *Agent // Escreve e revisa código (Programador) Executor *Agent // Roda ferramentas e APIs (Executor) } Cada agente tem uma responsabilidade única. O planejador não programa. O programador não executa. O executor não planeja. Eles se especializam, se comunicam e, juntos, resolvem problemas que sobrecarregariam qualquer agente individual.\nPor que isso importa para o navi-agent Quando comecei a projetar o navi-agent, cometi o erro do agente primeiro. Construí um agente monolítico que tentava lidar com orquestração, execução de ferramentas, gerenciamento de memória e formatação de respostas, tudo de uma vez.\nFoi uma bagunça.\nA virada de chave veio quando percebi: O navi-agent não é um agente. O navi-agent é uma agência. É um sistema onde agentes especializados trabalham juntos, cada um com limites e propósitos claros.\nAspecto Agente Agência Escopo Tarefa única Fluxo de trabalho coordenado Tomada de Decisão Reativa Estratégica + Reativa Modo de Falha Tudo ou nada Degradação graciosa (Graceful degradation) Escalabilidade Limitada Horizontal Manutenção Difícil de debugar Responsabilidade clara No momento em que você entende essa diferença, toda a sua abordagem para orquestração de IA muda. Você para de perguntar \u0026ldquo;Como deixo meu agente mais inteligente?\u0026rdquo; e começa a perguntar \u0026ldquo;Como faço meus agentes trabalharem melhor juntos?\u0026rdquo;.\nEssa é a base sobre a qual o navi-agent foi construído.\nOs Pontos Fracos dos LLMs Eu amo essa tecnologia. Eu a estudei. Eu criei coisas com ela. Estou construindo o navi-agent em cima dela. Mas aqui está o que aprendi da pior maneira: se você não entende onde os LLMs quebram, você não está construindo infraestrutura — está construindo um castelo de cartas.\nDeixa eu compartilhar o que descobri depois de inúmeras sessões de debug às 4 da manhã.\nA Barreira de Contexto Que Ninguém Comenta Todo mundo comemora janelas de contexto maiores como se tivéssemos resolvido tudo. Legal, mas aqui está o que realmente acontece: seu modelo começa a esquecer coisas antes mesmo de atingir o limite.\nEu estava construindo uma funcionalidade onde o navi-agent precisava lembrar do histórico da conversa, mais os resultados das ferramentas, mais as instruções do sistema. Parece simples, certo? Bem, por volta do token 8.000 em um modelo de 32K, as coisas ficaram esquisitas. O modelo começou a:\nIgnorar instruções que coloquei no início Começar a dar respostas genéricas Alucinar com mais frequência Perder o controle das restrições É como quando você estuda por 8 horas seguidas e, na hora 7, você está apenas\u0026hellip; lendo palavras sem absorver nada.\n1 2 3 4 5 6 7 // O que eu achei que funcionaria func BuildContext(history, tools, instructions string) string { return instructions + history + tools // Concatenação simples, certo? } // O que realmente acontece dentro do modelo // Diluição de atenção = 📉 A lição? Mais contexto ≠ mais inteligência. Às vezes, mais contexto = mais confusão.\nO Problema da Confiança LLMs não fazem ideia de quando estão errados. Nenhuma. Zero. Eles vão te dar a resposta mais confiantemente incorreta com 99% de certeza.\nFiz um teste onde fiz a mesma pergunta 100 vezes com pequenas variações. O modelo dava respostas contraditórias, mas soando absolutamente certo em todas as vezes. Foi aí que me dei conta: confiança ≠ exatidão.\n1 2 3 4 5 type LLMResponse struct { Answer string Confidence float64 // Este número não significa nada IsCorrect bool // O modelo não sabe disso } Se você está construindo sistemas que executam código, lidam com dinheiro ou tocam em segurança — e está confiando na confiança auto-relatada do modelo — cara, você vai ter problemas.\nA Memória Que Não Existe LLMs não têm memória. Eles não aprendem com as conversas. Eles não atualizam seu conhecimento. Após cada resposta, é como se nascessem de novo — uma amnésia pura, mas cheia de marra.\nTudo o que você acha que é \u0026ldquo;memória\u0026rdquo; é, na verdade, construído pelo desenvolvedor. Sem uma arquitetura de memória externa, você tem um peixinho dourado com um PhD. Brilhante, mas esquece tudo em 3 segundos.\nA Armadilha do Generalista LLMs sabem tudo e nada ao mesmo tempo. Perguntar sobre Kubernetes, física quântica, roteiros de anime e história medieval na mesma conversa? Sem problema. Mas se aprofunde em qualquer um desses tópicos e você encontrará os limites.\nEles são generalistas estatísticos. Reconhecedores de padrões anabolizados. O que é incrível para amplitude, mas perigoso quando você precisa de profundidade.\nAprendi isso quando estava fazendo vibe coding com revisão ativa usando os modelos Claude Opus. Ele gerava um código que parecia perfeito, mas tinha bugs sutis. O modelo conhecia a sintaxe, entendia o padrão, mas deixava passar os edge cases (casos extremos) porque ele não entende código de verdade — ele prevê tokens com base em padrões que já viu.\nA Realidade da Alucinação Vamos ser claros: alucinação não é um bug, é uma feature. Bom, não exatamente uma feature, mas um subproduto inevitável de como esses modelos funcionam. Eles preveem tokens de forma probabilística. Às vezes, essa previsão está errada. E eles não fazem a menor ideia de quando isso acontece.\nO que piora a situação:\nInstruções ambíguas Contexto conflitante Excesso de informação Perguntas sobre coisas que não existem 1 2 3 4 // A parte assustadora response := model.Ask(\u0026#34;Algo que não existe\u0026#34;) fmt.Println(response) // Retorna algo plausível fmt.Println(model.KnowsItsWrong()) // false - esse método não existe Por Que Sistemas Multi-Agentes Realmente Importam Então, por que falar tanto sobre fraquezas? Porque entendê-las muda tudo sobre como você constrói as coisas.\nA maioria das pessoas acha que sistemas multi-agentes são sobre velocidade. Paralelização. Fazer as coisas mais rápido. Elas estão erradas.\nÉ sobre distribuição cognitiva.\nQuando dividi o navi-agent em agentes especializados, algo interessante aconteceu:\nAgente Único Agência Diluição de contexto Contexto focado por agente Alta taxa de alucinação Menor alucinação por agente Difícil de debugar Limites de falha claros Respostas genéricas Saídas especializadas Tudo falha junto Degradação graciosa (Graceful degradation) Cada agente opera em um escopo cognitivo menor. Escopo menor significa menos confusão, melhor adesão às instruções e debug mais fácil.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // Antes: Um agente tentando fazer tudo type GodAgent struct { // Planejamento // Pesquisa // Programação // Execução // Verificação // Memória // Tudo... } // Depois: Agentes especializados type Agency struct { Planner *Agent // Apenas planeja Researcher *Agent // Apenas pesquisa Coder *Agent // Apenas programa Executor *Agent // Apenas executa Verifier *Agent // Apenas verifica } Uma agência não é necessariamente mais rápida por padrão. Ela é mais estável. Velocidade é um efeito colateral. Estabilidade é o objetivo.\nA Decisão pela Arquitetura Hexagonal O cenário de IA está em constante fluxo. Novas versões de API, capacidades de modelos, provedores e tecnologias surgem todo mês.\nEntão, como isso se conecta à arquitetura? Simples: se os LLMs são inerentemente voláteis e o campo da IA evolui tão rapidamente, sua arquitetura precisa ser desacoplada para proteger seu sistema dessas mudanças.\nA arquitetura hexagonal (portas e adaptadores) me dá:\nFronteiras claras entre a lógica do LLM e a lógica de negócios Interfaces testáveis sem precisar chamar modelos reais Implementações intercambiáveis (troque implementações sem alterar o núcleo) Isolamento de componentes propensos a alucinações 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Porta: O que o LLM pode fazer type LLMPort interface { Generate(ctx context.Context, prompt Prompt) (Response, error) Embed(ctx context.Context, text string) (Vector, error) } // Adaptador: Como nós realmente fazemos isso (OpenAI, Anthropic, local, etc.) type OpenAIAdapter struct { client *openai.Client } // Núcleo: Lógica de negócios que não se importa com qual é o LLM type Orchestrator struct { llm LLMPort // Não sabe nem se importa com a implementação } Isso não é apenas código limpo — é sobrevivência. Vamos ser realistas sobre o que pode mudar nos próximos 12 meses:\nMudanças tecnológicas — A OpenAI muda sua API. A Anthropic lança um novo protocolo. Sua arquitetura determina se isso será apenas uma atualização de configuração numa tarde de terça-feira ou dias reescrevendo código. Cenários de fallback (contingência) — A OpenAI cai. Você atinge os limites de taxa (rate limits). Sua chave de API é estrangulada (throttled). Com arquitetura hexagonal, seu orquestrador não se importa — ele apenas chama a porta, e o adaptador lida com o failover. Implantações híbridas — Alguns agentes rodam localmente por privacidade, outros na nuvem por potência. Alguns usam OpenAI, outros usam Claude, outros usam modelos open-source que você mesmo hospeda. Mesma interface, adaptadores diferentes. 1 2 3 4 5 6 7 8 9 // O orquestrador não sabe nem se importa type Orchestrator struct { llm LLMPort // Pode ser OpenAI // Pode ser Anthropic // Pode ser sua instância local do Ollama // Pode ser um load balancer distribuindo entre 5 provedores // Não importa. A interface continua a mesma. } É por isso que a arquitetura hexagonal não é overengineering (excesso de engenharia) — é reconhecer que o cenário dos LLMs vai mudar. A questão não é se você precisará se adaptar. É se a sua arquitetura permite que você se adapte em horas ou em meses.\nO Que Eu Aprendi LLMs são ferramentas, não pensadores — Eles são reconhecedores de padrões incrivelmente poderosos, mas não \u0026ldquo;entendem\u0026rdquo; as coisas como nós. A arquitetura importa mais do que nunca — Uma arquitetura ruim com LLMs não apenas quebra, ela quebra de forma imprevisível. Especialização vence a generalização — Tanto para agentes quanto para os sistemas que os contêm. Supervisão humana é inegociável — Por mais autônomo que seja o seu sistema, mantenha humanos no circuito (human-in-the-loop) para decisões críticas. O ciclo do hype é real — Ignore o barulho de \u0026ldquo;A IA vai substituir os desenvolvedores\u0026rdquo;. Construa coisas úteis. Resolva problemas reais. O Que Vem a Seguir para o navi-agent Ainda estou no início dessa jornada. A arquitetura está se estabilizando, mas ainda há muito código para escrever e decisões para tomar, como:\nSandbox Gerenciamento de memória com vector stores (bancos de dados vetoriais) Camadas de segurança contra prompt injection Observabilidade para as interações entre agentes Fluxos de trabalho de autocura (self-healing workflows) Interfaces human-in-the-loop Mas a fundação é sólida. E é sólida porque a projetei em torno dos pontos fracos dos LLMs, não de seus pontos fortes.\nSe você está construindo com LLMs, eu adoraria ouvir suas histórias de trincheira. Quais erros de arquitetura você cometeu? O que funcionou? O que falhou miseravelmente?\nMe dê um toque nos comentários, no X ou no Discord. E se você estiver curioso sobre o progresso do navi-agent, o repositório no GitHub é onde toda a experimentação bagunçada acontece em público (ainda não há código lá, mas em breve terá).\nLembre-se: a infraestrutura sobrevive a bolhas. O hype não.\n","permalink":"https://enrell.github.io/pt/posts/navi-arquitecture/","summary":"\u003cp\u003eEram 3 da manhã quando tive a ideia do navi-agent, alguns meses atrás. Eu estava na cama pensando sobre o impacto dos LLMs nas \u003cem\u003ehard skills\u003c/em\u003e dos desenvolvedores. Antes do boom dos LLMs, eu melhorava minhas habilidades de programação construindo projetos para o meu próprio uso. Mas quando a OpenAI lançou o GPT-3, vi que essa tecnologia poderia ser útil. Passei muito tempo brincando com a geração de código do GPT-3, e lembro da sensação que tive quando o usei para aprender POO (Programação Orientada a Objetos). Eu fiquei tipo: \u0026ldquo;Que p*rra é essa! Como esses caras fazem isso?\u0026rdquo;. Essa foi a faísca que fez meu hiperfoco ativar para estudar a área.\u003c/p\u003e","title":"Definindo as decisões de arquitetura do navi-agent"},{"content":"Hello world guys! O TLDR é: Eu testei o openclaw e outros orquestradores de AI e eles sempre seguem o mesmo padrão:\nEles são criados como um produto para venda, e não como um projeto open-source para a comunidade. Eles são criados pelo hype e para o hype, com uma ideia genérica de agência, um produto que tem um monte de features e skills que no final das contas, não são tão úteis assim. Isso ocorre porque não são criados para resolver problemas reais, e sim para o hype, marketing e para vender assinaturas das big techs. Por isso a OpenAI contratou o Peter Steinberger, um acqui-hire clássico para ter um meio de vender suas chaves de API e subscriptions, e não para resolver problemas reais.\nPosso citar exemplos de projetos que seguem o mesmo padrão:\nWindsurf Adept Covariant Todos hypados como “próxima geração de agents/coding/robotics”. Google, Amazon e Meta fazem licensing e pegam fundadores/time.\nResultado: startups viram “ghost company” ou skeleton crew. Funcionários choram em all-hands, o produto morre ou fica irrelevante, enquanto o time vai trabalhar pros modelos/cloud da big tech.\nO diferencial do navi-agent O propósito do navi-agent é ser um orquestrador de agentes útil, seguro e para a comunidade tech/dev.\nEu levantei pontos inúteis do openclaw, e você deve se perguntar o porquê de eu estar tacando o pau no openclaw, e não em outros orquestradores de AI. A resposta é simples: porque o openclaw é o mais popular, o mais hypado, o mais vendido e o mais usado, e por isso ele é o melhor exemplo para ilustrar. Quem é da bolha tech e nunca ouviu falar de openclaw nas últimas semanas está morando em uma caverna.\nEu não nego que estou em uma bolha, e que os LLMs formam uma bolha que pode estourar a qualquer momento, e que o AI winter vai chegar cedo ou tarde. Mas o que eu quero dizer é que, mesmo que seja uma bolha, mesmo que o hype seja grande e mesmo que as big techs estejam investindo pesado, algumas empresas vão sobreviver e modelos open-source vão continuar existindo. Isso porque, de fato, há um valor real e uma demanda real; só não é o tipo de valor e demanda que as big techs estão vendendo, e sim uma demanda mais nichada, mais específica, mais real e mais útil. É isso que o navi-agent tem como objetivo: sobreviver ao estouro da bolha.\nEu acredito que a bolha vai estourar, e que as empresas que sobreviverem vão ser aquelas que realmente entregarem valor, seja ele um produto, um serviço ou modelos de base. LLM é uma tecnologia que tem utilidade, principalmente nessas áreas (não está em ordem):\nA utilidade dos agentes Desenvolvimento de software: Nunca foi tão fácil tirar um projeto do papel. Testar ideias, aprender um framework novo ou só meter um \u0026ldquo;vibe coding\u0026rdquo; pra ver se um produto para em pé ficou bizarramente rápido. A armadilha aqui é pra quem só copia e cola sem entender o que tá rolando por baixo dos panos. (Se quiser fugir disso e aprender algo útil, veja esse post: como usar LLMs da forma correta).\nSuporte ao cliente sem dor de cabeça: Esqueça aqueles bots burros de antigamente. Um agente inteligente integrado no WhatsApp ou nos sistemas internos resolve de 60% a 80% das buchas diárias 24/7. Tenho amigos que já tramparam em suporte e a real é uma só: 90% dos problemas são coisas banais, muitas vezes de idosos ou de quem tem zero letramento digital. A IA resolve isso sem suar.\nVendas e Marketing em escala (sem parecer um robô): Dá pra automatizar desde a qualificação de leads (SDR agents) até a recuperação de carrinhos perdidos e análise de calls. É gerar conteúdo e mandar emails personalizados em massa, mas que ainda soam naturais.\nAchar as coisas na própria empresa (o famoso RAG): Sabe aquela documentação legada gigantesca ou as políticas internas que ninguém sabe onde estão? Bota um chat interno apontado pra isso. Se bem implementado, documentar e consultar o conhecimento da empresa vira algo trivial.\nCibersegurança e Blue/White Hats: Analisar log na mão é chato e repetitivo. Um modelo bem tunado consegue mastigar logs, detectar ameaças, fazer um pentest superficial e gerar relatório rapidinho. É uma mão na roda absurda pra quem trabalha com segurança e precisa reduzir o tempo de resposta a incidentes.\nApesar de todos os benefícios que os agentes trazem, a maioria das pessoas tem uma visão exagerada sobre eles e, na maioria das vezes, dão superpoderes que esses agentes não deveriam ter. A pegadinha é achar que agentes resolvem tudo. Isso não poderia estar mais errado: eles devem fazer parte do processo, e não tomar o processo inteiro para si.\nA segurança Uma das coisas mais tristes na bolha tech atualmente é a negligência com a segurança. Não precisa ser nível enterprise, mas deve ser sólida; tenha o mínimo de responsabilidade. Não faça como certos devs: não suba um projeto de hobby com portas abertas, credenciais em plaintext e Remote Code Execution de um clique. Mesmo avisar explicitamente várias vezes não é o suficiente, é a receita para o desastre.\nnavi-agent project Depois desse extensivo desabafo, está na hora de falar sobre o projeto que vem pulsando na minha mente, o navi-agent. A ideia surgiu há anos como um projeto pessoal (claramente estou atrasado, não? XD). Eu sempre busquei a automatização no meu ambiente de desenvolvimento, fossem aplicações auto-hospedadas, ferramentas CLI ou scripts de automação, mas nunca fiquei satisfeito em como a automação era feita, e eu descobri o porquê.\nA automação de tarefas, principalmente no Linux, é extremamente descentralizada. Isso significa que temos muitas ferramentas para automação, mas elas não se comunicam de forma nativa e padronizada. Você tem um script bash para uma coisa, um cronjob para outra, e várias ferramentas CLI excelentes que exigem que você faça um \u0026ldquo;glue code\u0026rdquo; (código cola) feio pra caramba para fazer uma conversar com a outra. As ferramentas não se conversam, e nem deveriam, por questão de segurança.\nO navi-agent nasce exatamente para ser esse elo, o maestro dessa orquestra, mas com uma regra de ouro: o controle é sempre seu e a segurança é inegociável. Ele não é um \u0026ldquo;agente autônomo mágico\u0026rdquo; que vai rodar um rm -rf / porque alucinou no meio de uma task ou interpretou mal um prompt solto.\nO nome veio do NAVI, um computador do anime Serial Experiments Lain (1998) É um dos meus animes favoritos e eu recomendo muito assistir: é denso, intelectual e filosófico. Enfim, esse computador é usado pela protagonista Lain para acessar a Wired (a rede global ultra-avançada do anime, tipo uma internet que mistura realidade virtual, consciência coletiva e muito mais). O NAVI é o conjunto de hardware + software para acessar a Wired. Ele tem uma interface por navegação e por voz.\nNAVI: Ela faz um upgrade insano no NAVI: A Arquitetura do navi-agent Para garantir que o projeto seja robusto, escalável e, acima de tudo, testável, eu escolhi escrever o navi-agent em Go. Além de entregar uma performance absurda e compilar tudo para um binário único (o que facilita muito a vida no Linux), Go me permite lidar com concorrência de forma muito elegante e direta.\nA base do projeto segue a Arquitetura Hexagonal (Ports and Adapters).\nIsso significa que o \u0026ldquo;core\u0026rdquo; da inteligência e orquestração do navi-agent é completamente isolado das ferramentas externas e das interfaces. Se amanhã eu quiser trocar o provedor de LLM, o banco de dados local ou a forma como ele executa um script no meu sistema operacional, eu só escrevo um novo \u0026ldquo;adapter\u0026rdquo;. A lógica de negócio continua intacta e isolada de efeitos colaterais.\nComo você interage com ele? Como eu falei no começo, o navi-agent não te prende a uma interface web pesada e proprietária que tenta te empurrar uma assinatura, nem é um agente que vai expor suas credenciais. Ele foi pensado para ter múltiplos pontos de entrada (os Ports da nossa arquitetura):\nTUI (Terminal User Interface): Para quem vive no terminal (e quem usa Neovim e um window manager como Sway sabe o valor de não precisar tirar a mão do teclado), ter uma interface rápida, bonita e responsiva direto no console é essencial.\nAPI REST/gRPC: Se você quiser integrar o navi-agent em outro sistema, criar um frontend próprio ou disparar webhooks de outras aplicações, a porta está aberta.\nBots de Mensageria: Integração nativa com Discord e Telegram. Você pode disparar automações no seu servidor ou na sua máquina de casa mandando uma mensagem pelo celular, de forma autenticada e segura.\nO roadmap ainda está sendo desenhado, e o core está sendo desenvolvido. A ideia é construir algo open-source, focando em resolver problemas reais de produtividade e orquestração de sistema, sem vender a alma para o hype.\nEm breve eu solto o repositório no GitHub para quem quiser dar uma olhada no código (ou dar uns pitacos nas PRs). Até lá, seguimos codando!\n\u0026ndash; Present day, present time! hahahahaha\n","permalink":"https://enrell.github.io/pt/posts/navi-the-ai-orchestrator/","summary":"\u003ch1 id=\"hello-world-guys\"\u003eHello world guys!\u003c/h1\u003e\n\u003cp\u003eO TLDR é: Eu testei o openclaw e outros orquestradores de AI e eles sempre seguem o mesmo padrão:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eEles são criados como um produto para venda, e não como um projeto open-source para a comunidade. Eles são criados pelo hype e para o hype, com uma ideia genérica de agência, um produto que tem um monte de features e skills que no final das contas, não são tão úteis assim. Isso ocorre porque não são criados para resolver problemas reais, e sim para o hype, marketing e para vender assinaturas das big techs.\nPor isso a OpenAI contratou o Peter Steinberger, um acqui-hire clássico para ter um meio de vender suas chaves de API e subscriptions, e não para resolver problemas reais.\u003c/p\u003e","title":"Eu estou fazendo o navi-agent: um orquestrador de AI seguro e útil de verdade | chora openclaw"},{"content":"Eram 3 da manhã de uma terça-feira. Eu estava olhando para a minha pasta de animes, rolando por nomes de arquivos como:\n[SubsPlease] Spy x Family - 01 (1080p) [A4DAF3D9].mkv [Coalgirls] Clannad (1920x1080 Blu-Ray FLAC) [1234ABCD]/[Coalgirls] Clannad - 01 (1920x1080 Blu-Ray FLAC) [1234ABCD].mkv One Punch Man S02E03 1080p WEBRip x264-PandoR.mkv E pensei comigo mesmo: \u0026ldquo;Tem que haver um jeito melhor.\u0026rdquo;\nSoa familiar? Se você já montou uma biblioteca de mídia, sabe exatamente do que estou falando. Esses nomes de arquivos bagunçados e inconsistentes — eles me deixam louco. E os parsers existentes? Ou eram muito lentos, muito rígidos ou não lidavam com a variedade selvagem de convenções de nomenclatura que nós, fãs de anime, usamos.\nEntão eu mesmo construí um.\nConheça o Zantetsu Zantetsu (japonês para \u0026ldquo;lâmina que corta ferro\u0026rdquo; — afiada, rápida, precisa) é a minha solução para este problema. É um parser de metadados de anime incrivelmente rápido que extrai o título, número do episódio, resolução, codecs e muito mais de qualquer nome de arquivo que você jogar nele.\nnpm install zantetsu Uma linha. Isso é tudo que você precisa para começar a fazer o parse.\nPor Que Isso Importa Aqui está o que o Zantetsu consegue fazer:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { parse } from \u0026#39;zantetsu\u0026#39;; const result = parse(\u0026#39;[SubsPlease] Spy x Family - 01 (1080p).mkv\u0026#39;); // Resultado: // { // title: \u0026#34;Spy x Family\u0026#34;, // episode: { type: \u0026#39;single\u0026#39;, episode: 1 }, // resolution: \u0026#39;FHD1080\u0026#39;, // group: \u0026#39;SubsPlease\u0026#39;, // video_codec: \u0026#39;H264\u0026#39;, // audio_codec: \u0026#39;AAC\u0026#39;, // source: \u0026#39;WEB\u0026#39;, // confidence: 0.85 // } Mas fica ainda melhor. O Zantetsu lida com o caos:\nArquivos com múltiplos episódios? ✅ Notações de temporada (S01E03)? ✅ Variantes de resolução (1080p, 1080i, 1080)? ✅ Release groups e codecs? ✅ Processamento em lote (batch) para pastas inteiras? ✅ A História Por Trás Disso Eu não acordei simplesmente e decidi escrever um parser. Esse projeto começou porque eu precisava dele para o meu próprio servidor de mídia. Eu estava cansado de renomear arquivos manualmente ou usar ferramentas desajeitadas que não conseguiam acompanhar as convenções criativas de nomenclatura da comunidade de anime.\nEntão fiz o que qualquer desenvolvedor faz: resolvi o meu próprio problema.\nMas eu não queria apenas mais um parser baseado em regex. Eu queria algo rápido. Algo que pudesse processar milhares de nomes de arquivos em segundos. É por isso que escolhi Rust para o motor principal — ele me dá performance nativa com as garantias de segurança que preciso.\nAs bindings para TypeScript? Isso é para a experiência do desenvolvedor. Porque fazer o parse deve ser agradável, não um pesadelo de debugging.\nA Stack Aqui está o que faz o Zantetsu funcionar:\nCamada Tecnologia Por quê Parser Principal Rust Velocidade bruta, segurança de memória Bindings TypeScript Experiência do desenvolvedor Build Cargo + npm O melhor dos dois mundos O resultado? Um pacote que é 10x mais rápido do que alternativas em JavaScript puro — mas que ainda parece JavaScript nativo na hora de usar.\nExemplo do Mundo Real Digamos que você tenha uma pasta com conteúdo misto:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { parseBatch } from \u0026#39;zantetsu\u0026#39;; const files = [ \u0026#39;[SubsPlease] Spy x Family - 01 (1080p).mkv\u0026#39;, \u0026#39;[Coalgirls] Clannad - 02 (720p) [ABC123].mkv\u0026#39;, \u0026#39;One Punch Man S02E03 1080p WEBRip.mkv\u0026#39;, \u0026#39;[Erai-raws] Made in Abyss S1 - 04 [1080p][Multiple Subtitle].mkv\u0026#39; ]; const results = parseBatch(files); // Processe-os como quiser results.forEach(r =\u0026gt; { console.log(`${r.title} - Episódio ${r.episode?.episode}`); }); Saída:\nSpy x Family - Episódio 1 Clannad - Episódio 2 One Punch Man - Episódio 3 Made in Abyss - Episódio 4 Lindo, não é?\nO Que Eu Aprendi Esta foi a minha primeira vez publicando um pacote NPM e, uau — há muito mais nisso do que eu esperava:\nVersionamento importa — Versionamento semântico não é opcional Definições de tipos são essenciais — Seus usuários vão te agradecer Documentação é uma feature — Passei tanto tempo na documentação quanto no código Testes não são negociáveis — Mínimo de 80% de cobertura de testes O feedback da comunidade vale ouro — Os primeiros usuários encontram bugs que você nunca imaginou O Que Vem a Seguir Estou apenas começando. Aqui está o que está no roadmap:\nParse turbinado por ML — Para os nomes de arquivos realmente bizarros Detecção de múltiplos episódios — Lidando com lançamentos em lote (batch) API de regras customizadas — Adicione seus próprios padrões de parse Mais tipos de mídia — Suporte para filmes, séries de TV, músicas Experimente Eu tornei o Zantetsu open-source porque acredito em retribuir à comunidade que o inspirou. Seja você alguém construindo um servidor de mídia, um indexador de torrents ou apenas precisando organizar sua pasta de animes — esta ferramenta é para você.\n1 npm install zantetsu E se você tiver problemas, encontrar um bug ou tiver um pedido de feature — o repositório no GitHub está sempre aberto. Eu adoraria ouvir o seu feedback.\nE quanto a você? Existe algum problema no seu fluxo de trabalho diário que você tem adiado para resolver? Deixe-me saber nos comentários — talvez juntos possamos construir algo incrível.\nAlém disso, se você achou isso útil, compartilhe com outros desenvolvedores. Ajuda mais do que você imagina.\n","permalink":"https://enrell.github.io/pt/posts/zantetsu-intro/","summary":"\u003cp\u003eEram 3 da manhã de uma terça-feira. Eu estava olhando para a minha pasta de animes, rolando por nomes de arquivos como:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[SubsPlease] Spy x Family - 01 (1080p) [A4DAF3D9].mkv\n[Coalgirls] Clannad (1920x1080 Blu-Ray FLAC) [1234ABCD]/[Coalgirls] Clannad - 01 (1920x1080 Blu-Ray FLAC) [1234ABCD].mkv\nOne Punch Man S02E03 1080p WEBRip x264-PandoR.mkv\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eE pensei comigo mesmo: \u003cem\u003e\u0026ldquo;Tem que haver um jeito melhor.\u0026rdquo;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eSoa familiar? Se você já montou uma biblioteca de mídia, sabe exatamente do que estou falando. Esses nomes de arquivos bagunçados e inconsistentes — eles me deixam louco. E os parsers existentes? Ou eram muito lentos, muito rígidos ou não lidavam com a variedade selvagem de convenções de nomenclatura que nós, fãs de anime, usamos.\u003c/p\u003e","title":"Eu Criei um Pacote NPM para Fazer o Parse de Nomes de Arquivos de Anime — Aqui Está a Minha História"},{"content":"Hello, world! Primeiramente, quero dizer que este é o meu primeiro post em muito tempo, e espero continuar escrevendo com mais frequência. Tenho trabalhado em um MCP (Model Context Protocol) há algum tempo e tenho aprendido muito sobre a manipulação de AST no TypeScript. Quero compartilhar um pouco dos meus aprendizados com vocês.\nO que é um MCP (Model Context Protocol)? O MCP é um protocolo aberto que permite padronizar a forma como as aplicações fornecem contexto aos seus modelos.\nIsso significa que você pode usar o MCP para fornecer contexto (dados) aos LLMs, e os LLMs poderão usar esse contexto para gerar respostas melhores.\nDigamos que você pergunte ao LLM \u0026ldquo;Como está o clima?\u0026rdquo; e o LLM não saiba a resposta, porque ele não tem acesso a dados em tempo real. Se você fornecer ao LLM o clima atual no Brasil, ele será capaz de gerar uma resposta correta, certo?\nBom, é exatamente disso que se trata o MCP. Ele permite que você forneça contexto aos LLMs para que eles possam gerar respostas melhores.\nVocê pode usar uma variedade de fontes de dados para fornecer contexto aos LLMs, incluindo bancos de dados, APIs e até inputs do usuário. A chave é garantir que o contexto seja relevante e atualizado para que os LLMs possam gerar as melhores respostas possíveis.\nPara este exemplo, você pode usar um servidor MCP simples que recebe o nome de uma cidade, por exemplo \u0026ldquo;São Paulo\u0026rdquo;, e retorna o clima atual nessa cidade.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from mcp.server.fastmcp.server import FastMCP import requests mcp = FastMCP() @mcp.tool() def get_weather(city: str, state: str, country: str) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34; Get the current weather for a given city, state, and country using OpenWeatherMap API. \u0026#34;\u0026#34;\u0026#34; API_KEY = \u0026#34;YOUR_API_KEY\u0026#34; # Step 1: Get latitude and longitude geo_url = f\u0026#34;http://api.openweathermap.org/geo/1.0/direct?q={city},{state},{country}\u0026amp;limit=1\u0026amp;appid={API_KEY}\u0026#34; geo_resp = requests.get(geo_url) geo_data = geo_resp.json() if not geo_data: return {\u0026#34;error\u0026#34;: \u0026#34;Location not found\u0026#34;} lat = geo_data[0][\u0026#34;lat\u0026#34;] lon = geo_data[0][\u0026#34;lon\u0026#34;] # Step 2: Get weather data weather_url = f\u0026#34;https://api.openweathermap.org/data/3.0/onecall?lat={lat}\u0026amp;lon={lon}\u0026amp;appid={API_KEY}\u0026amp;units=metric\u0026#34; weather_resp = requests.get(weather_url) weather_data = weather_resp.json() if \u0026#34;current\u0026#34; not in weather_data: return {\u0026#34;error\u0026#34;: \u0026#34;Weather data not found\u0026#34;} return { \u0026#34;city\u0026#34;: city, \u0026#34;temperature\u0026#34;: weather_data[\u0026#34;current\u0026#34;][\u0026#34;temp\u0026#34;], \u0026#34;description\u0026#34;: weather_data[\u0026#34;current\u0026#34;][\u0026#34;weather\u0026#34;][0][\u0026#34;description\u0026#34;].capitalize() } Com este servidor MCP, você pode fornecer contexto aos LLMs chamando o método get_weather com o nome da cidade e do país. O servidor então retornará o clima atual nessa cidade.\nExemplo de uma resposta 1 2 3 4 5 { \u0026#34;city\u0026#34;: \u0026#34;Sao Paulo\u0026#34;, \u0026#34;temperature\u0026#34;: 25.5, \u0026#34;description\u0026#34;: \u0026#34;Sunny\u0026#34; } Com essa resposta, o LLM será capaz de gerar uma resposta melhor para a pergunta \u0026ldquo;Como está o clima em São Paulo?\u0026rdquo; usando o contexto fornecido pelo servidor MCP, que obtém as informações climáticas atuais da API do OpenWeatherMap.\nAST (Abstract Syntax Tree) AST (Abstract Syntax Tree - Árvore Sintática Abstrata) é uma representação da estrutura do código-fonte. É uma estrutura de dados em formato de árvore que representa a sintaxe do código de uma forma que seja fácil de analisar e manipular.\nO AST é usado em muitas linguagens de programação para representar a estrutura do código e é uma ferramenta poderosa para analisar e manipular código.\nSite do Typescript AST viewer\nPor que estou estudando MCP e AST? Porque estou trabalhando no ast-mcp, um MCP para operações em AST, que fornece um servidor para que os LLMs interajam com ASTs.\nO projeto ainda está em seus estágios iniciais, mas espero torná-lo uma ferramenta poderosa para que desenvolvedores possam analisar e manipular código usando uma abordagem mais eficiente.\nEspero que você tenha achado este post útil e mal posso esperar para compartilhar mais aprendizados com vocês no futuro. Se você tiver alguma dúvida ou sugestão, sinta-se à vontade para me chamar no X ou no Discord.\nSe quiser contribuir com o projeto, fique à vontade para abrir uma issue no repositório do GitHub.\n","permalink":"https://enrell.github.io/pt/posts/mcp-ast-learnings/","summary":"\u003ch1 id=\"hello-world\"\u003eHello, world!\u003c/h1\u003e\n\u003cp\u003ePrimeiramente, quero dizer que este é o meu primeiro post em muito tempo, e espero continuar escrevendo com mais frequência. Tenho trabalhado em um MCP (Model Context Protocol) há algum tempo e tenho aprendido muito sobre a manipulação de AST no TypeScript. Quero compartilhar um pouco dos meus aprendizados com vocês.\u003c/p\u003e\n\u003ch1 id=\"o-que-é-um-mcp-model-context-protocol\"\u003eO que é um MCP (Model Context Protocol)?\u003c/h1\u003e\n\u003cp\u003eO MCP é um protocolo aberto que permite padronizar a forma como as aplicações fornecem contexto aos seus modelos.\u003c/p\u003e","title":"Compartilhando alguns aprendizados sobre MCP e AST"}]