<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel><title>Apuntes de eduardófilo</title><description>Apuntes varios sobre algunas de las cosas que necesito recordar, principalmente sobre Desarrollo y Sistemas.</description><link>https://apuntes.eduardofilo.es/</link><atom:link href="https://apuntes.eduardofilo.es/rss.xml" rel="self" type="application/rss+xml" /><managingEditor>Eduardo Moreno Lamana</managingEditor><docs>https://github.com/eduardofilo/eduardofilo.mkdocs</docs><language>es</language> <pubDate>Sat, 08 Aug 2026 10:23:00 -0000</pubDate> <lastBuildDate>Sat, 08 Aug 2026 10:23:00 -0000</lastBuildDate> <ttl>1440</ttl> <generator>MkDocs RSS plugin - v1.15.0</generator> <item> <title>Instalación OpenCode, OmniRoute y Ponytail</title> <description>&lt;p&gt;&lt;img alt=&#34;OpenCode + OmniRoute&#34; src=&#34;images/posts/2026-07-26_opencode_omniroute/opencode_omniroute.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;El desarrollo asistido por IA empezó siendo un autocompletado inteligente del código a nivel de línea. Pasó a ser una &#34;segunda opinión&#34; que revisaba el código a nivel de fichero y sugería cambios. En los últimos dos años se ha dado un importante paso más allá hasta llegar al paradigma actual, el &lt;em&gt;desarrollo agéntico&lt;/em&gt;: un agente que trabaja a nivel de proyecto, edita ficheros, ejecuta comandos, realiza por sí solo los cambios que antes solo se sugerían, lanza los tests e itera hasta cerrar la tarea. El problema es que todas las herramientas comerciales de este tipo (Claude Code, Cursor, Devin, Copilot, Codex, etc.) comparten dos limitaciones: una suscripción mensual y unas cuotas que se agotan justo cuando la sesión se está poniendo interesante. Porque aquí no consideramos el uso por medio de API que para el ámbito personal resulta inasequible.&lt;/p&gt;&lt;p&gt;Hay además un tercer problema, más silencioso pero también caro: los agentes tienden a &lt;strong&gt;sobre-construir&lt;/strong&gt;. Se les pide un selector de fechas y acaban instalando una librería, escribiendo un componente envoltorio y una hoja de estilos. Cada línea de más se paga dos veces, en tokens al generarla y en tokens cada vez que vuelve a entrar en el contexto.&lt;/p&gt;&lt;p&gt;En este artículo se describe cómo montar un entorno de desarrollo agéntico completo combinando tres proyectos de código abierto que resuelven precisamente estas limitaciones:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://opencode.ai/&#34;&gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/a&gt;: el agente de programación en terminal.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://omniroute.online/&#34;&gt;&lt;strong&gt;OmniRoute&lt;/strong&gt;&lt;/a&gt;: el router/gateway de modelos que alimenta al agente.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/DietrichGebert/ponytail&#34;&gt;&lt;strong&gt;Ponytail&lt;/strong&gt;&lt;/a&gt;: el &lt;em&gt;skill&lt;/em&gt; que impone al agente disciplina de código mínimo.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;descripcion-de-los-productos&#34;&gt;Descripción de los productos&lt;a class=&#34;headerlink&#34; href=&#34;#descripcion-de-los-productos&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&#34;opencode&#34;&gt;OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OpenCode es un agente de programación que se ejecuta en la terminal (TUI), de código abierto y, lo más importante para nuestro propósito, &lt;strong&gt;agnóstico respecto al proveedor de modelos&lt;/strong&gt;. A diferencia de Claude Code (atado a Anthropic) o Codex (atado a OpenAI), OpenCode permite configurar cualquier proveedor, incluidos los que exponen una API compatible con OpenAI, que a día de hoy son prácticamente todos.&lt;/p&gt;&lt;p&gt;Sus características principales:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Interfaz de terminal con sesiones persistentes, gestión de contexto y &lt;em&gt;undo&lt;/em&gt; de los cambios aplicados.&lt;/li&gt;&lt;li&gt;Acceso a herramientas: lectura y escritura de ficheros, ejecución de comandos, búsqueda en el repositorio, control de versiones.&lt;/li&gt;&lt;li&gt;Fichero de contexto por proyecto (&lt;code&gt;AGENTS.md&lt;/code&gt;) que se genera con &lt;code&gt;/init&lt;/code&gt; y que sirve para instruir al agente sobre las convenciones del repositorio.&lt;/li&gt;&lt;li&gt;Soporte de MCP (&lt;em&gt;Model Context Protocol&lt;/em&gt;) para añadir herramientas externas.&lt;/li&gt;&lt;li&gt;Modo CLI no interactivo (&lt;code&gt;opencode run &#34;...&#34;&lt;/code&gt;) para automatizaciones y scripts.&lt;/li&gt;&lt;li&gt;Agentes especializados y modos de trabajo (por ejemplo un modo &lt;em&gt;plan&lt;/em&gt; que no hace cambios pensado para la fase de planificación previa).&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;omniroute&#34;&gt;OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OmniRoute es un &lt;em&gt;gateway&lt;/em&gt; de IA local y de código abierto. Se instala en la propia máquina (o en un VPS), levanta un endpoint compatible con la API de OpenAI y un panel web de administración, y desde ahí enruta cada petición hacia cualquiera de los cientos de proveedores que tiene catalogados, de los que unos 90 disponen de capa gratuita.&lt;/p&gt;&lt;p&gt;Lo interesante no es el catálogo en sí, sino lo que hace con él:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Enrutado automático&lt;/strong&gt;: usando el modelo virtual &lt;code&gt;auto&lt;/code&gt;, OmniRoute construye un &lt;em&gt;combo&lt;/em&gt; con los proveedores que tengamos conectados y elige uno en cada petición según su puntuación en vivo. Existen variantes: &lt;code&gt;auto/coding&lt;/code&gt; (calidad para generar código), &lt;code&gt;auto/fast&lt;/code&gt; (menor latencia), &lt;code&gt;auto/cheap&lt;/code&gt; (menor coste por token) y &lt;code&gt;auto/offline&lt;/code&gt; (mayor margen de cuota disponible).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fallback en cascada&lt;/strong&gt;: cuando un proveedor devuelve un error de cuota, un &lt;em&gt;rate limit&lt;/em&gt; o un 4xx-5xx, la petición se reintenta contra el siguiente proveedor de la lista de forma transparente para el cliente. Esto es lo que evita el clásico &#34;se acabó tu cuota, vuelve en cinco horas&#34;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Compresión de contexto&lt;/strong&gt;: una batería de motores que recortan el prompt (deduplicación, poda de historial, compactado de ficheros) y que permiten ahorros importantes de tokens, especialmente valiosos cuando se trabaja contra capas gratuitas medidas en tokens/día.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Panel de control&lt;/strong&gt; en &lt;code&gt;http://localhost:20128&lt;/code&gt; con estadísticas de uso por proveedor, gestión de claves API y configuración asistida de las herramientas CLI más habituales.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Local-first&lt;/strong&gt;: las claves de los proveedores y el tráfico se quedan en la máquina; OmniRoute no es un servicio en la nube al que haya que suscribirse.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;ponytail&#34;&gt;Ponytail&lt;a class=&#34;headerlink&#34; href=&#34;#ponytail&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail es un &lt;strong&gt;&lt;em&gt;skill&lt;/em&gt;&lt;/strong&gt;, es decir un conjunto de instrucciones que se inyectan en el contexto del agente en cada turno para modificar su comportamiento. Su cometido es uno solo, combatir la sobre-ingeniería, y lo hace obligando al agente a recorrer una escalera de decisión antes de escribir una sola línea, deteniéndose en el primer peldaño que resuelva el problema:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;1. ¿Esto necesita existir? → no: no lo hagas (YAGNI)2. ¿Ya está en este repositorio? → reutilízalo, no lo reescribas3. ¿Lo hace la librería estándar? → úsala4. ¿Es una función nativa de la plataforma? → úsala5. ¿Hay una dependencia ya instalada? → úsala6. ¿Cabe en una línea? → una línea7. Sólo entonces: lo mínimo que funcione&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;El ejemplo canónico del proyecto, al que hacíamos referencia en la introducción, lo resume bien: ante la petición de un selector de fechas, el resultado deja de ser una librería más un componente y pasa a ser, gracias a Ponytail, &lt;code&gt;&amp;lt;input type=&#34;date&#34;&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Dos matices importantes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;La escalera se aplica &lt;strong&gt;después&lt;/strong&gt; de entender el problema, no en lugar de entenderlo. El agente sigue obligado a leer el código afectado y a seguir el flujo real antes de elegir peldaño. Perezoso con la solución, nunca con la lectura.&lt;/li&gt;&lt;li&gt;Hay cuatro cosas que nunca se recortan: validación en las fronteras de confianza, gestión de pérdida de datos, seguridad y accesibilidad. La regla no es &#34;menos tokens&#34;, es &#34;sólo lo que la tarea necesita&#34;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Según el &lt;em&gt;benchmark&lt;/em&gt; que publica el proyecto (sesiones reales de un agente sobre un repositorio FastAPI + React, con y sin el &lt;em&gt;skill&lt;/em&gt;), los resultados frente a la línea base son un 54% menos de líneas de código, un 22% menos de tokens, un 20% menos de coste y un 27% menos de tiempo, sin penalización en las comprobaciones de seguridad. Como siempre con los &lt;em&gt;benchmarks&lt;/em&gt;, conviene tomarlos como orden de magnitud y no como promesa: el ahorro es enorme donde hay una trampa clara de sobre-construcción y casi nulo donde el código ya era mínimo.&lt;/p&gt;&lt;p&gt;Ponytail funciona en un buen número de agentes (Claude Code, Codex, Copilot CLI, Gemini CLI, Cursor, Devin...), y OpenCode es uno de los que tiene soporte de primera clase mediante &lt;em&gt;plugin&lt;/em&gt;.&lt;/p&gt;&lt;h3 id=&#34;el-valor-de-la-combinacion&#34;&gt;El valor de la combinación&lt;a class=&#34;headerlink&#34; href=&#34;#el-valor-de-la-combinacion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Por separado cada pieza es útil, pero es al combinarlas cuando desaparecen las limitaciones que mencionábamos al principio. Cada una cubre una capa distinta y no se solapan: OmniRoute decide &lt;strong&gt;quién&lt;/strong&gt; responde, OpenCode decide &lt;strong&gt;qué&lt;/strong&gt; se hace, y Ponytail decide &lt;strong&gt;cuánto&lt;/strong&gt; código se escribe.&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Problema&lt;/th&gt;&lt;th&gt;Cómo lo resuelve la combinación&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Suscripción mensual&lt;/td&gt;&lt;td&gt;OpenCode es gratuito y OmniRoute se conecta a capas gratuitas de decenas de proveedores. El coste de entrada es 0 €.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Agotamiento de cuota&lt;/td&gt;&lt;td&gt;El fallback de OmniRoute salta al siguiente proveedor sin interrumpir la sesión del agente.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dependencia de un proveedor&lt;/td&gt;&lt;td&gt;El agente sólo conoce un endpoint (&lt;code&gt;localhost:20128/v1&lt;/code&gt;); cambiar de modelo o de proveedor no requiere tocar el agente.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Consumo excesivo de tokens&lt;/td&gt;&lt;td&gt;Los motores de compresión de OmniRoute reducen el tamaño de los prompts que envía el agente, que suelen ser grandes por el contexto del repositorio.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Falta de visibilidad del gasto&lt;/td&gt;&lt;td&gt;El panel de OmniRoute centraliza el consumo de todos los proveedores y de todas las herramientas conectadas.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Código sobredimensionado&lt;/td&gt;&lt;td&gt;Ponytail recorta lo que el agente construye, lo que a su vez reduce el código a revisar, mantener y volver a meter en el contexto en las siguientes sesiones.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;La combinación de las tres piezas tiene un efecto multiplicador sobre la cuota disponible: Ponytail reduce los tokens de salida (menos código generado), la compresión de OmniRoute reduce los de entrada (menos contexto enviado) y el fallback aprovecha la cuota de todos los proveedores conectados.&lt;/p&gt;&lt;p&gt;Un beneficio adicional: como OmniRoute expone un endpoint estándar, el mismo gateway sirve simultáneamente a OpenCode en la terminal, a una extensión de VSCode o a cualquier otro cliente compatible con la API de OpenAI. Se configura una vez y lo aprovecha todo el entorno.&lt;/p&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Modelos modestos, mejores resultados&lt;/p&gt;&lt;p&gt;Un efecto colateral interesante de Ponytail es que hace más viable trabajar con los modelos gratuitos, que suelen ser más pequeños. Cuanto menor es el volumen de código que se le pide generar a un modelo, menos oportunidades tiene de equivocarse; el &lt;em&gt;skill&lt;/em&gt; reduce precisamente el tamaño de lo que se le encarga en cada paso.&lt;/p&gt;&lt;/div&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Sobre las capas gratuitas&lt;/p&gt;&lt;p&gt;Las condiciones de las capas gratuitas de los proveedores cambian con frecuencia y algunas desaparecen sin previo aviso. La ventaja del enfoque con OmniRoute es que esos cambios se absorben en el gateway (conectando otro proveedor) sin tocar la configuración del agente. Conviene también revisar las políticas de privacidad de cada proveedor gratuito antes de enviarles el código de un proyecto sensible.&lt;/p&gt;&lt;/div&gt;&lt;h2 id=&#34;instalacion&#34;&gt;Instalación&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Todo el procedimiento se ha realizado sobre Linux. En Windows y MacOS los pasos son equivalentes, cambiando la forma de instalar los requisitos.&lt;/p&gt;&lt;p&gt;Los tres proyectos se distribuyen vía npm, así que el único requisito común es disponer de Node.js reciente (versión 22 o superior).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;node&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;instalacion-de-opencode&#34;&gt;Instalación de OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-de-opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;La forma recomendada es el script oficial de instalación:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-fsSL&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://opencode.ai/install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Alternativamente, según el sistema:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-g&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode-ai&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# npm (también bun, pnpm o yarn)&lt;/span&gt;brew&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;anomalyco/tap/opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# MacOS / Linux con Homebrew&lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Arch Linux (repositorios oficiales)&lt;/span&gt;paru&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode-bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Arch Linux (AUR, última versión)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Comprobamos la instalación lanzando el agente dentro de cualquier directorio:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;En este punto OpenCode arrancará pero pedirá credenciales de algún proveedor. No hace falta darle ninguna todavía: ese hueco lo va a ocupar OmniRoute. Por ahora simplemente salimos del programa.&lt;/p&gt;&lt;h3 id=&#34;instalacion-de-omniroute&#34;&gt;Instalación de OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-de-omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-g&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omnirouteomniroute&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;El comando &lt;code&gt;omniroute&lt;/code&gt; levanta a la vez la API y el panel web en el puerto &lt;code&gt;20128&lt;/code&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Panel: &lt;code&gt;http://localhost:20128&lt;/code&gt;&lt;/li&gt;&lt;li&gt;API compatible con OpenAI: &lt;code&gt;http://localhost:20128/v1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Existen también otras formas de despliegue, útiles si se quiere dejar el gateway funcionando de forma permanente:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# contenedor&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Además del paquete npm y el contenedor, el proyecto distribuye una aplicación de escritorio (Electron), imágenes para arm64 (funciona en una Raspberry Pi) y es ejecutable en Android bajo Termux. Una opción interesante es dejar OmniRoute corriendo en un VPS o en un servidor casero y apuntar contra él desde todos los equipos de desarrollo.&lt;/p&gt;&lt;h3 id=&#34;instalacion-de-ponytail&#34;&gt;Instalación de Ponytail&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-de-ponytail&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail no se instala en el sistema, se declara como &lt;em&gt;plugin&lt;/em&gt; de OpenCode. Basta con añadir esta entrada al fichero de configuración &lt;code&gt;opencode.json&lt;/code&gt; (en el apartado siguiente se muestra el fichero completo con esta y el resto de piezas):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@dietrichgebert/ponytail&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;OpenCode descarga el paquete desde npm la primera vez que arranca con esa configuración. Si se prefiere trabajar sobre una copia local del repositorio, para poder modificar las reglas, se clona y se apunta al fichero del &lt;em&gt;plugin&lt;/em&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/DietrichGebert/ponytail.git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/ponytail&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;/home/usuario/ponytail/.opencode/plugins/ponytail.mjs&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Las rutas relativas (del tipo &lt;code&gt;./.opencode/plugins/ponytail.mjs&lt;/code&gt;) se resuelven respecto al &lt;code&gt;opencode.json&lt;/code&gt; que las declara, de modo que para compartir un mismo &lt;em&gt;checkout&lt;/em&gt; entre varios proyectos hay que usar la ruta absoluta, como en el ejemplo.&lt;/p&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Sin plugin también funciona&lt;/p&gt;&lt;p&gt;El repositorio incluye las reglas en formato &lt;code&gt;AGENTS.md&lt;/code&gt; y en los formatos de rules de Cursor, Devin, Cline, Kiro, Copilot, etc. Copiando ese fichero al proyecto se obtiene el comportamiento siempre activo en prácticamente cualquier agente. Lo que añade el &lt;em&gt;plugin&lt;/em&gt; de OpenCode son los comandos &lt;code&gt;/ponytail&lt;/code&gt; y los niveles de intensidad.&lt;/p&gt;&lt;/div&gt;&lt;h2 id=&#34;configuracion-conjunta&#34;&gt;Configuración conjunta&lt;a class=&#34;headerlink&#34; href=&#34;#configuracion-conjunta&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;La integración consiste en que OpenCode deje de hablar con los proveedores y hable únicamente con OmniRoute, y en que Ponytail se inyecte en cada turno de la conversación. Son cinco pasos.&lt;/p&gt;&lt;h3 id=&#34;1-conectar-proveedores-en-omniroute&#34;&gt;1. Conectar proveedores en OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#1-conectar-proveedores-en-omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Con &lt;code&gt;omniroute&lt;/code&gt; en marcha, abrimos &lt;code&gt;http://localhost:20128&lt;/code&gt; y vamos a la sección &lt;strong&gt;Providers&lt;/strong&gt; (recomiendo configurar el interfaz en idioma &lt;code&gt;English&lt;/code&gt; ya que si no aparecen numerosas cadenas precedidas de la partícula &lt;code&gt;__MISSING__&lt;/code&gt;; también utilizar el campo de búsqueda, ya que el número de grupos de configuración de OmniRoute es enorme). Ahí aparece el catálogo con un indicador de cuáles tienen capa gratuita. Conviene conectar &lt;strong&gt;varios&lt;/strong&gt;, ya que el valor del sistema está justamente en la redundancia: cuando uno agota su cuota, el resto siguen disponibles. Los proveedores sin necesidad de clave (&lt;code&gt;No Auth&lt;/code&gt;) están conectados por defecto.&lt;/p&gt;&lt;p&gt;El catálogo está clasificado por &lt;strong&gt;categorías&lt;/strong&gt;, y la categoría marca el procedimiento de conexión:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Categoría&lt;/th&gt;&lt;th&gt;Cómo se conecta&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;No Auth&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Están activos desde el primer arranque. Aunque desactivaremos algunos como luego veremos.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Free Tier&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Capa gratuita que sí requiere darse de alta en el proveedor y pegar una clave de API.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;OAuth&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Botón de inicio de sesión para proveedores en los que ya tengamos cuenta: OmniRoute abre el flujo del proveedor y guarda el &lt;em&gt;token&lt;/em&gt; resultante.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;API Key&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Proveedores de pago (algunos con crédito inicial de regalo); se pega la clave.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Local&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Modelos que corren en la propia máquina (Ollama, LM Studio, vLLM…); se indica la URL local.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;El filtro de la parte superior de la pantalla permite quedarse sólo con las que interesan, y el buscador acepta el nombre o el identificador del proveedor.&lt;/p&gt;&lt;h4 id=&#34;proveedores-sin-autenticacion&#34;&gt;Proveedores sin autenticación&lt;a class=&#34;headerlink&#34; href=&#34;#proveedores-sin-autenticacion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;En general la elección y configuración de los proveedores es la parte más complicada, farragosa y que más tiempo consume de todo el proceso descrito en este artículo.&lt;/p&gt;&lt;p&gt;No solo eso, algunos de los proveedores activados por defecto como los de la categoría &#34;No Auth&#34;, pueden provocar problemas. Durante las primeras sesiones de uso de OpenCode observé que la mayoría de las peticiones obtenían una respuesta vacía. Tras investigar encontré que el problema era la intervención de proveedor &#34;Augment (Auggie CLI)&#34; que por tanto recomiendo desactivar.&lt;/p&gt;&lt;p&gt;Además mirando los logs de la consola de OmniRoute (&lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt;), encontré que los proveedores &#34;Chipotle Pepper AI (Free)&#34; y &#34;DuckDuckGo AI Chat&#34; fallaban siempre, por lo que aunque no introducen respuestas incorrectas como &#34;Augment (Auggie CLI)&#34;, retrasan el recorrido de la cascada de proveedores, por lo que recomiendo desactivarlos también. El proveedor &#34;Veo AI Free&#34; lo desactivaremos, dado que sólo ofrece modelos de vídeo que no nos van a servir para codificar.&lt;/p&gt;&lt;h4 id=&#34;proveedores-con-clave-de-api-gratuita&#34;&gt;Proveedores con clave de API gratuita&lt;a class=&#34;headerlink&#34; href=&#34;#proveedores-con-clave-de-api-gratuita&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;El procedimiento es siempre el mismo, cambiando únicamente el sitio del que se obtiene la clave:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Darse de alta en el proveedor y generar una clave de API en su panel (el enlace suele estar en la propia ficha del proveedor en OmniRoute).&lt;/li&gt;&lt;li&gt;En OmniRoute, &lt;strong&gt;Providers&lt;/strong&gt; → buscar el proveedor → botón de conectar/configurar.&lt;/li&gt;&lt;li&gt;Pegar la clave en el formulario y guardar.&lt;/li&gt;&lt;li&gt;Pulsar el botón de &lt;strong&gt;Test&lt;/strong&gt; de la tarjeta del proveedor. Si responde correctamente, sus modelos pasan a formar parte del catálogo del gateway y entran automáticamente en el &lt;em&gt;pool&lt;/em&gt; del modelo &lt;code&gt;auto&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Los proveedores de esta categoría son los que más nos van a permitir aprovechar las ventajas de OmniRoute. Son numerosos, pero como se comentaba en el apartado anterior, requiere bastante trabajo configurarlos (obtención de API Keys de distintos proveedores) y mantenerse al día de cuáles cambian en operativa o son más o menos recomendables en un momento dado.&lt;/p&gt;&lt;p&gt;Algunos documentos que seguir para mantener en buena forma la lista de proveedores gratuitos son los siguientes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/wiki/Providers-Guide#best-free-providers&#34;&gt;Providers Guide - Best Free Providers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.49/docs/getting-started/FREE-TIERS-GUIDE.md&#34;&gt;FREE TIERS GUIDE&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.49/docs/reference/FREE_TIERS.md&#34;&gt;FREE TIERS&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Algunos de los proveedores de este tipo que me parecen recomendables en el momento de escribir este artículo son:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://aistudio.google.com/api-keys&#34;&gt;Gemini (Google AI Studio)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://groq.com/&#34;&gt;Groq&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://console.mistral.ai/&#34;&gt;Mistral&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://dash.cloudflare.com/&#34;&gt;Cloudflare Workers AI&lt;/a&gt;: Tras introducir la API Key, se produce el intento de conexión con los modelos que fallará indicando que hace falta un &#34;Account ID&#34; además. El &#34;Account ID&#34; es el hash que aparece a continuación de la URL &lt;code&gt;https://dash.cloudflare.com&lt;/code&gt; una vez creada e iniciada la sesión. Introduciremos ese hash en el campo &#34;Account ID&#34; de los ajustes del provider.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://enter.pollinations.ai/&#34;&gt;Pollinations AI&lt;/a&gt;: Aunque en el cuadro de conexión se indica que la API Key es opcional, es mejor crear una.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;proveedores-oauth&#34;&gt;Proveedores OAuth&lt;a class=&#34;headerlink&#34; href=&#34;#proveedores-oauth&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Con estos no hay clave que copiar: se pulsa el botón de conexión de la tarjeta, se abre el flujo de inicio de sesión del proveedor en el navegador, y al terminar OmniRoute guarda las credenciales cifradas en su base de datos local. Es el caso de las cuentas de herramientas que ya se estén usando (Copilot, Cursor, Kilo Code, Qoder, etc.).&lt;/p&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Revisa los términos de servicio&lt;/p&gt;&lt;p&gt;Varios de estos proveedores son en realidad servicios pensados para consumirse desde su propio cliente, y algunos prohíben expresamente en sus condiciones el uso a través de &lt;em&gt;proxies&lt;/em&gt; o clientes de terceros. La ficha del proveedor en OmniRoute lo advierte cuando es el caso. Conviene leer esos avisos antes de conectar una cuenta que interese conservar.&lt;/p&gt;&lt;/div&gt;&lt;h4 id=&#34;depurar-el-pool-para-uso-agentico&#34;&gt;Depurar el pool para uso agéntico&lt;a class=&#34;headerlink&#34; href=&#34;#depurar-el-pool-para-uso-agentico&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Hay una cuestión que no aparece en la documentación de OmniRoute y que, en mi experiencia, ha resultado fundamental para que el montaje resulte operativo. Se trata de que &lt;strong&gt;no todos los proveedores del catálogo sirven para alimentar a un agente&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;La razón es que un agente como OpenCode no se limita a pedir texto: envía en cada petición la lista de &lt;em&gt;herramientas&lt;/em&gt; de las que dispone (leer ficheros, ejecutar comandos, buscar en el repositorio…) y espera que el modelo responda con llamadas a esas herramientas. Los proveedores que son la API oficial de un modelo devuelven esas llamadas al cliente, que es lo correcto. Pero el catálogo incluye también dos tipos de entradas que no se comportan así:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Proxies de un cliente web&lt;/strong&gt; (los que se conectan con la cookie de sesión de un chat, en lugar de con una clave de API). El servicio del otro lado tiene su propio motor de herramientas e intenta ejecutarlas él, en su entorno, en lugar de devolverlas.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Front-ends de agentes&lt;/strong&gt; (servicios pensados para consumirse desde su propia herramienta de línea de comandos). Traen su propio catálogo de herramientas y no encajan como motor de otro agente.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;El síntoma es desconcertante, porque el agente no da ningún error: simplemente devuelve una respuesta vacía, o un texto en el que el modelo explica que no ha podido explorar el repositorio y nos pide, por ejemplo, que le peguemos nosotros el contenido de los ficheros.&lt;/p&gt;&lt;h5 id=&#34;un-test-de-admision&#34;&gt;Un test de admisión&lt;a class=&#34;headerlink&#34; href=&#34;#un-test-de-admision&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;La forma fiable de decidir si un proveedor entra o no en el pool es preguntárselo al gateway: enviar una petición con una herramienta y comprobar si el modelo la invoca. La respuesta debe contener un bloque &lt;code&gt;tool_calls&lt;/code&gt; con el nombre de la herramienta y sus argumentos; si en su lugar llega prosa, ese proveedor no sirve.&lt;/p&gt;&lt;p&gt;No basta con lanzar una petición contra &lt;code&gt;auto/coding&lt;/code&gt; y dar por bueno el pool. El enrutado de &lt;code&gt;auto&lt;/code&gt; tiende a pegarse al último proveedor que funcionó y no garantiza que todos los candidatos reciban tráfico. Hay que probar cada proveedor por separado.&lt;/p&gt;&lt;p&gt;El comportamiento problemático que queremos detectar (proxies que ejecutan las herramientas ellos mismos, front-ends con su propio catálogo) es una propiedad &lt;strong&gt;del proveedor&lt;/strong&gt;, no del modelo individual: un proxy o pasa las &lt;code&gt;tool_calls&lt;/code&gt; al cliente o no las pasa, independientemente del modelo que tenga detrás. Por eso basta con probar &lt;strong&gt;un modelo representativo por proveedor&lt;/strong&gt;, identificable por el prefijo antes de la barra (&lt;code&gt;groq/&lt;/code&gt;, &lt;code&gt;mistral/&lt;/code&gt;, &lt;code&gt;cf/&lt;/code&gt;...), en lugar de los cientos de modelos del catálogo. En realidad probaremos los 5 primeros modelos de cada proveedor por si alguno falla por alguna razón específica. El filtro de &lt;code&gt;jq&lt;/code&gt; excluye sólo los modelos virtuales &lt;code&gt;auto/*&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;36&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;38&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;39&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;40&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;API&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-tu-clave-omniroute&amp;quot;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Lista de proveedores (excluyendo auto/*)&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;providers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/models&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[.data[].id | select(startswith(&amp;quot;auto/&amp;quot;) | not)]&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; | group_by(split(&amp;quot;/&amp;quot;)[0]) | map(.[0]) | .[]&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cut&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-f1&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sort&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-u&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;prov&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$providers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Hasta 5 modelos de este proveedor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;prov_models&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/models&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--arg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[.data[].id | select(startswith($p+&amp;quot;/&amp;quot;))][:5][]&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tested&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov_models&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tested&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$model&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/chat/completions&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;{\&amp;quot;model\&amp;quot;:\&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$model&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;\&amp;quot;,\&amp;quot;stream\&amp;quot;:false,\&amp;quot;messages\&amp;quot;:[{\&amp;quot;role\&amp;quot;:\&amp;quot;user\&amp;quot;,\&amp;quot;content\&amp;quot;:\&amp;quot;Lee el fichero /etc/hostname\&amp;quot;}],\&amp;quot;tools\&amp;quot;:[{\&amp;quot;type\&amp;quot;:\&amp;quot;function\&amp;quot;,\&amp;quot;function\&amp;quot;:{\&amp;quot;name\&amp;quot;:\&amp;quot;read\&amp;quot;,\&amp;quot;description\&amp;quot;:\&amp;quot;Lee un fichero\&amp;quot;,\&amp;quot;parameters\&amp;quot;:{\&amp;quot;type\&amp;quot;:\&amp;quot;object\&amp;quot;,\&amp;quot;properties\&amp;quot;:{\&amp;quot;path\&amp;quot;:{\&amp;quot;type\&amp;quot;:\&amp;quot;string\&amp;quot;}},\&amp;quot;required\&amp;quot;:[\&amp;quot;path\&amp;quot;]}}}]}&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;http_code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$resp&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;head&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-1&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-o&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[0-9][0-9][0-9]&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$resp&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sed&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/^\r\?$/,$p&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;tail&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+2&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;200&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$body&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-e&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.choices[0].message.tool_calls[0].function.name&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;/dev/null&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FALLA (sin tool_calls)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;401&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;403&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FALLA (auth &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# 404, 400... modelo no disponible, probar el siguiente&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-z&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FALLA (sin modelos válidos)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%-24s %-45s %s\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tested&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Requiere &lt;code&gt;jq&lt;/code&gt;. El script distingue tres causas de fallo:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FALLA (sin tool_calls)&lt;/code&gt;&lt;/strong&gt;: el proveedor respondió con un 200 pero devolvió prosa en lugar de una llamada a la herramienta. Es el problema que venimos buscando: proxy que ejecuta las tools él mismo o front-end con su propio catálogo. =&amp;gt; Desconectar.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FALLA (auth 401/403)&lt;/code&gt;&lt;/strong&gt;: la clave del proveedor está caducada, mal configurada o sin cuota. No es un problema de &lt;em&gt;tools&lt;/em&gt;, sino de configuración. =&amp;gt; Hay que arreglar la conexión en &lt;strong&gt;Providers&lt;/strong&gt; (o desconectarla si ya no tiene remedio).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FALLA (sin modelos válidos)&lt;/code&gt;&lt;/strong&gt;: todos los modelos probados devolvieron 404 o 400. El proveedor está caído o su catálogo ha cambiado. =&amp;gt; Revisar en &lt;strong&gt;Providers&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;En la salida, la columna de la derecha muestra el modelo con el que se obtuvo el resultado. Es un trabajo que se hace una vez, y que hay que repetir sólo al conectar un proveedor nuevo.&lt;/p&gt;&lt;h5 id=&#34;dos-sintomas-que-reconocer-en-los-logs&#34;&gt;Dos síntomas que reconocer en los logs&lt;a class=&#34;headerlink&#34; href=&#34;#dos-sintomas-que-reconocer-en-los-logs&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;En &lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt; hay dos patrones que delatan a un proveedor que conviene retirar:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;200&lt;/code&gt; con &lt;code&gt;TO: 0&lt;/code&gt;&lt;/strong&gt; en unos pocos milisegundos: el proveedor ha devuelto una respuesta vacía con estado de éxito. Una inferencia real nunca tarda 15 ms.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Respuestas lentas cuyo contenido es texto&lt;/strong&gt; donde debería haber &lt;code&gt;tool_calls&lt;/code&gt;, a menudo con mensajes de error del propio servicio remoto explicando que las herramientas no existen.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;El fallback no salta con los fallos disfrazados de éxito&lt;/p&gt;&lt;p&gt;Conviene entender bien el límite del sistema. La cascada de reintentos de OmniRoute funciona con los fallos &lt;strong&gt;explícitos&lt;/strong&gt;: un 429 por cuota, un 502 o un timeout hacen que la petición se reintente contra el siguiente proveedor, y eso se aprecia perfectamente en el registro. Pero los dos casos anteriores llegan al gateway como respuestas correctas: un &lt;code&gt;200&lt;/code&gt; con un cuerpo vacío es, para un router, una respuesta válida. Ningún &lt;em&gt;gateway&lt;/em&gt; puede arbitrar eso sin inspeccionar semánticamente el contenido de cada respuesta.&lt;/p&gt;&lt;p&gt;De ahí que la curación del pool sea responsabilidad nuestra, y que sea un paso que conviene no saltarse. Dicho de otro modo: &lt;strong&gt;el enrutado automático es tan bueno como el peor proveedor aparentemente sano del pool&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt;&lt;h3 id=&#34;2-crear-una-clave-de-api-de-omniroute&#34;&gt;2. Crear una clave de API de OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#2-crear-una-clave-de-api-de-omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;En el panel, sección &lt;strong&gt;API Manager&lt;/strong&gt; → &lt;strong&gt;Create API Key&lt;/strong&gt;. Le damos un nombre descriptivo (por ejemplo &lt;code&gt;opencode&lt;/code&gt;) y copiamos la clave generada, con formato &lt;code&gt;sk-xxxxxxxx-xxxxxxxx&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Esta clave es local: es la que usarán nuestros clientes (OpenCode en este caso) para autenticarse contra OmniRoute, y no tiene nada que ver con las claves de los proveedores, que quedan custodiadas por OmniRoute.&lt;/p&gt;&lt;p&gt;Podemos verificar que el gateway responde y ver qué modelos ofrece:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;http://localhost:20128/v1/models&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer sk-tu-clave-omniroute&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;3-declarar-omniroute-como-proveedor-en-opencode&#34;&gt;3. Declarar OmniRoute como proveedor en OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#3-declarar-omniroute-como-proveedor-en-opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OpenCode admite cualquier proveedor compatible con OpenAI declarándolo en su fichero de configuración. Para que la configuración aplique a todos los proyectos, la escribimos en &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;$schema&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;https://opencode.ai/config.json&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@dietrichgebert/ponytail&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;provider&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;omniroute&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;npm&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@ai-sdk/openai-compatible&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;OmniRoute&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;baseURL&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;apiKey&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-tu-clave-omniroute&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;models&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (equilibrado)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/coding&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (calidad para código)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/cheap&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (menor coste)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/fast&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (menor latencia)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/offline&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (máxima cuota disponible)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;model&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;omniroute/auto/coding&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Los puntos importantes de esta configuración:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;npm&lt;/code&gt;: el adaptador &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt; es el que se usa con endpoints &lt;code&gt;/v1/chat/completions&lt;/code&gt;, que es el caso de OmniRoute.&lt;/li&gt;&lt;li&gt;&lt;code&gt;options.baseURL&lt;/code&gt;: el endpoint del gateway. Si OmniRoute está en otra máquina, aquí va su IP o dominio.&lt;/li&gt;&lt;li&gt;&lt;code&gt;options.apiKey&lt;/code&gt;: la clave de API que creamos en OmniRoute.&lt;/li&gt;&lt;li&gt;&lt;code&gt;models&lt;/code&gt;: no hace falta enumerar modelos concretos de proveedores; basta con declarar los modelos virtuales &lt;code&gt;auto&lt;/code&gt;, que son los que activan el enrutado inteligente y el fallback. Si se quiere fijar un modelo concreto, se puede añadir con el identificador que devuelve &lt;code&gt;/v1/models&lt;/code&gt;, la consulta del &lt;a href=&#34;#2-crear-una-clave-de-api-de-omniroute&#34;&gt;punto anterior&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;model&lt;/code&gt;: modelo por defecto al arrancar. &lt;code&gt;auto/coding&lt;/code&gt; es una buena elección para desarrollo; &lt;code&gt;auto/offline&lt;/code&gt; resulta útil cuando lo prioritario es no quedarse sin cuota.&lt;/li&gt;&lt;li&gt;&lt;code&gt;plugin&lt;/code&gt;: la lista de &lt;em&gt;plugins&lt;/em&gt; de OpenCode, donde entra Ponytail.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Clave fuera del fichero de configuración&lt;/p&gt;&lt;p&gt;Si el fichero de configuración se va a versionar (por ejemplo en un repositorio de &lt;em&gt;dotfiles&lt;/em&gt;), es preferible no escribir la clave en él. OpenCode permite almacenar credenciales con el comando &lt;code&gt;/connect&lt;/code&gt; (opción &lt;em&gt;Other&lt;/em&gt;, id &lt;code&gt;omniroute&lt;/code&gt;), en cuyo caso se puede omitir &lt;code&gt;options.apiKey&lt;/code&gt; del JSON.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;También es posible ubicar el fichero como &lt;code&gt;opencode.json&lt;/code&gt; en la raíz de un proyecto concreto, en cuyo caso su configuración se combina con la global. Esto resulta práctico para forzar un modelo distinto en un proyecto determinado.&lt;/p&gt;&lt;h3 id=&#34;4-ajustar-el-nivel-de-ponytail&#34;&gt;4. Ajustar el nivel de Ponytail&lt;a class=&#34;headerlink&#34; href=&#34;#4-ajustar-el-nivel-de-ponytail&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail no necesita fichero de configuración: funciona nada más declararlo como &lt;em&gt;plugin&lt;/em&gt;. Lo único que conviene decidir es el &lt;strong&gt;nivel de intensidad&lt;/strong&gt;, que se cambia en caliente desde la TUI:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/ponytail → informa del nivel actual/ponytail lite → recorte suave/ponytail full → nivel por defecto/ponytail ultra → recorte agresivo/ponytail off → desactivado para esta sesión&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;El nivel por defecto es &lt;code&gt;full&lt;/code&gt;. Para fijar otro en todas las sesiones nuevas se usa la variable de entorno &lt;code&gt;PONYTAIL_DEFAULT_MODE&lt;/code&gt; o el campo &lt;code&gt;defaultMode&lt;/code&gt; de &lt;code&gt;~/.config/ponytail/config.json&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PONYTAIL_DEFAULT_MODE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;full&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Mientras está activo, el conjunto de reglas se inyecta también en los subagentes que lance el agente principal. Si interesa excluir a alguno (por ejemplo los subagentes de búsqueda, que no escriben código), se puede acotar con una expresión regular contra el tipo de subagente definida en la siguiente variable de entorno:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PONYTAIL_SUBAGENT_MATCHER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;build|code&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;5-verificar-la-integracion&#34;&gt;5. Verificar la integración&lt;a class=&#34;headerlink&#34; href=&#34;#5-verificar-la-integracion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Arrancamos el agente y comprobamos que aparece el proveedor:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Dentro de la TUI, el comando &lt;code&gt;/models&lt;/code&gt; debe mostrar las entradas de OmniRoute.&lt;/p&gt;&lt;p&gt;Desde un directorio que contenga un repositorio o un proyecto, ejecutamos una prueba rápida en modo CLI no interactivo:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Resume en tres líneas qué hace este repositorio&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute/auto/coding&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Mientras se ejecuta, en el panel de OmniRoute puede verse en tiempo real qué proveedor ha atendido la petición (&lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt;), cuántos tokens se han consumido y cuántos ha ahorrado la compresión. Si un proveedor falla, en el registro se aprecia el salto automático al siguiente.&lt;/p&gt;&lt;p&gt;Para comprobar que Ponytail está cargado basta con ejecutar &lt;code&gt;/ponytail&lt;/code&gt; en la TUI, que responde con el nivel activo, o &lt;code&gt;/ponytail-help&lt;/code&gt;, que lista sus comandos.&lt;/p&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Aparente ERROR de arranque de Ponytail&lt;/p&gt;&lt;p&gt;Si se arranca OpenCode con &lt;code&gt;--print-logs&lt;/code&gt; puede aparecer esta línea al cargar la configuración:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ERROR message=&amp;quot;failed to load plugin&amp;quot; path=@dietrichgebert/ponytail error=&amp;quot;path must be a string or a file descriptor&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Es engañosa: OpenCode intenta cargar el plugin por dos rutas y una de ellas falla, pero la otra lo carga correctamente. La prueba de que Ponytail está operativo es que &lt;code&gt;/ponytail&lt;/code&gt; responda con el nivel activo. Hay que tener en cuenta además que el nivel se &lt;strong&gt;persiste&lt;/strong&gt; entre sesiones (en &lt;code&gt;~/.config/opencode/.ponytail-active&lt;/code&gt;): un &lt;code&gt;/ponytail off&lt;/code&gt; de una prueba anterior deja el plugin cargado pero desactivado. Ante la duda: &lt;code&gt;/ponytail full&lt;/code&gt; y preguntar al agente qué dice su &lt;em&gt;system prompt&lt;/em&gt; sobre &lt;code&gt;PONYTAIL&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Variables de entorno para el resto de herramientas&lt;/p&gt;&lt;p&gt;Exportando estas variables en el &lt;code&gt;.bashrc&lt;/code&gt; o &lt;code&gt;.zshrc&lt;/code&gt;, cualquier otra herramienta que respete la convención de OpenAI usará también el gateway sin configuración adicional:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-tu-clave-omniroute&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&#34;tipos-de-proyectos-y-operativa&#34;&gt;Tipos de proyectos y operativa&lt;a class=&#34;headerlink&#34; href=&#34;#tipos-de-proyectos-y-operativa&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Una vez montado el entorno, la operativa es esencialmente la misma con independencia del lenguaje, del tamaño del proyecto o la naturaleza de la tarea. Lo que cambia entre unos casos y otros no es el procedimiento, sino el modelo que conviene seleccionar, el nivel de recorte que interesa y la cantidad de contexto que hay que preparar. Por eso describimos primero el flujo común y después los ajustes por tipo de proyecto.&lt;/p&gt;&lt;h3 id=&#34;operativa-comun&#34;&gt;Operativa común&lt;a class=&#34;headerlink&#34; href=&#34;#operativa-comun&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Situarse en el repositorio y arrancar el agente&lt;/strong&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;mi-proyectoopencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generar el fichero de contexto&lt;/strong&gt; la primera vez, con el comando &lt;code&gt;/init&lt;/code&gt; dentro de la TUI. OpenCode explora el repositorio y escribe un &lt;code&gt;AGENTS.md&lt;/code&gt; con la descripción del proyecto, los comandos de compilación y test y las convenciones detectadas. Merece la pena revisarlo y completarlo a mano: es el documento que más influye en la calidad de los resultados posteriores, y conviene versionarlo con el proyecto.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Planificar antes de ejecutar&lt;/strong&gt;. Para cualquier cambio que no sea trivial, pedir primero un plan en lugar de la implementación directa. OpenCode dispone de un modo de planificación que no escribe en disco, y revisar el plan cuesta mucho menos que revisar un &lt;em&gt;diff&lt;/em&gt; equivocado de 400 líneas.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterar en pasos pequeños&lt;/strong&gt;, validando con las herramientas del propio proyecto (compilador, &lt;em&gt;linter&lt;/em&gt;, tests) tras cada paso. El agente puede ejecutar esos comandos por sí mismo si están documentados en &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pasar la revisión de Ponytail&lt;/strong&gt; antes de dar la tarea por buena, con &lt;code&gt;/ponytail-review&lt;/code&gt;. Analiza el &lt;em&gt;diff&lt;/em&gt; actual buscando sobre-ingeniería y devuelve una lista de cosas a borrar. Es el paso que más rápido amortiza el tiempo invertido.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Revisar el &lt;code&gt;diff&lt;/code&gt; y confirmar&lt;/strong&gt; con el control de versiones. Conviene trabajar siempre sobre una rama y hacer &lt;em&gt;commits&lt;/em&gt; pequeños: es la red de seguridad natural cuando quien escribe el código es un agente.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limpiar el contexto entre tareas&lt;/strong&gt; con &lt;code&gt;/new&lt;/code&gt;. Arrastrar el contexto de una tarea terminada empeora los resultados y dispara el consumo de tokens. En el apartado siguiente se detalla la gestión de sesiones.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vigilar el panel de OmniRoute&lt;/strong&gt; de vez en cuando para ver qué proveedores se están usando, cuáles se han agotado y si el ahorro por compresión es el esperado.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;gestion-de-sesiones-en-opencode&#34;&gt;Gestión de sesiones en OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#gestion-de-sesiones-en-opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Al empezar a trabajar con OpenCode sorprende comprobar que, por mucho que se salga de la TUI con &lt;code&gt;Ctrl-d&lt;/code&gt; o con &lt;code&gt;/exit&lt;/code&gt;, las sesiones siguen apareciendo en el listado. No es un problema de cierre incompleto ni un proceso que se quede colgado: es que &lt;strong&gt;una sesión no es un proceso, es una conversación persistida&lt;/strong&gt;. Salir de la TUI termina la interfaz, pero el historial de la conversación (mensajes, herramientas ejecutadas, ficheros tocados, consumo de tokens) se guarda en la base de datos local de OpenCode para poder retomarlo después. Y como cada invocación de &lt;code&gt;opencode&lt;/code&gt; sin argumentos abre una sesión nueva, en un par de días de uso el listado tiene decenas de entradas.&lt;/p&gt;&lt;p&gt;Dicho de otra forma: &#34;cerrar&#34; no significa &#34;borrar&#34;. Lo que hay que adquirir es la costumbre de reutilizar y de podar.&lt;/p&gt;&lt;h4 id=&#34;retomar-en-lugar-de-crear&#34;&gt;Retomar en lugar de crear&lt;a class=&#34;headerlink&#34; href=&#34;#retomar-en-lugar-de-crear&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Lo primero es dejar de arrancar siempre en blanco. Estas son las tres formas de continuar un trabajo previo:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--continue&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# retoma la última sesión del proyecto actual (-c)&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# retoma una sesión concreta (-s)&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--fork&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# la bifurca en una copia, dejando la original intacta&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Y desde dentro de la TUI, el comando &lt;code&gt;/sessions&lt;/code&gt; (alias &lt;code&gt;/resume&lt;/code&gt; y &lt;code&gt;/continue&lt;/code&gt;, atajo &lt;code&gt;Ctrl-x l&lt;/code&gt;) abre el selector para saltar de una sesión a otra sin salir del programa.&lt;/p&gt;&lt;p&gt;La opción &lt;code&gt;--fork&lt;/code&gt; es especialmente útil cuando una conversación ha llegado a un punto interesante y se quiere probar dos caminos distintos: se bifurca y cada rama sigue por su lado, igual que con &lt;code&gt;git branch&lt;/code&gt;.&lt;/p&gt;&lt;h4 id=&#34;cerrar-bien-cada-tarea&#34;&gt;Cerrar bien cada tarea&lt;a class=&#34;headerlink&#34; href=&#34;#cerrar-bien-cada-tarea&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Dentro de la TUI hay dos comandos que marcan el final de una tarea:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;/new&lt;/code&gt; (alias &lt;code&gt;/clear&lt;/code&gt;, atajo &lt;code&gt;Ctrl-x n&lt;/code&gt;): abre una sesión nueva y limpia. Es lo que hay que usar al cambiar de tarea, en lugar de seguir escribiendo en la conversación anterior.&lt;/li&gt;&lt;li&gt;&lt;code&gt;/compact&lt;/code&gt; (alias &lt;code&gt;/summarize&lt;/code&gt;, atajo &lt;code&gt;Ctrl-x c&lt;/code&gt;): resume la conversación actual para liberar ventana de contexto sin perder el hilo. Es la alternativa cuando se quiere continuar con la misma tarea pero el contexto se ha vuelto pesado.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Si de una sesión interesa conservar el resultado, &lt;code&gt;/export&lt;/code&gt; vuelca la conversación a Markdown y la abre en el editor, y &lt;code&gt;opencode export &amp;lt;sessionID&amp;gt;&lt;/code&gt; hace lo propio en JSON desde la línea de comandos.&lt;/p&gt;&lt;h4 id=&#34;inventariar-y-podar&#34;&gt;Inventariar y podar&lt;a class=&#34;headerlink&#34; href=&#34;#inventariar-y-podar&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Desde fuera de la TUI se gestiona el inventario completo:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# listado de sesiones&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# limitar el número de entradas&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--format&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;json&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# salida procesable por script&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;delete&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# eliminar una sesión&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Y para ver el coste de lo acumulado, que en este montaje es lo que determina cuánta cuota gratuita se está quemando:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# consumo de tokens y coste&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--days&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--models&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# últimos 7 días, desglosado por modelo&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--project&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# sólo el proyecto actual&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h4 id=&#34;flujo-de-trabajo&#34;&gt;Flujo de trabajo&lt;a class=&#34;headerlink&#34; href=&#34;#flujo-de-trabajo&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Reuniendo lo anterior, la rutina que mantiene el listado bajo control es:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Una sesión por tarea&lt;/strong&gt;, no por jornada ni por proyecto. La unidad natural es lo que acaba en un &lt;em&gt;commit&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Retomar con &lt;code&gt;-c&lt;/code&gt;&lt;/strong&gt; cuando se vuelve a una tarea a medias, en vez de arrancar &lt;code&gt;opencode&lt;/code&gt; a secas y volver a explicar el contexto (que además se paga en tokens).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/new&lt;/code&gt; al cambiar de tarea&lt;/strong&gt; y &lt;code&gt;/compact&lt;/code&gt; si la tarea se alarga pero sigue siendo la misma.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Exportar lo que merezca la pena&lt;/strong&gt; antes de dar por cerrada una sesión con conclusiones valiosas.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Poda periódica&lt;/strong&gt;, por ejemplo semanal, revisando &lt;code&gt;opencode session list&lt;/code&gt; y borrando con &lt;code&gt;opencode session delete&lt;/code&gt; todo lo que ya esté commiteado y no aporte nada.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Revisar &lt;code&gt;opencode stats&lt;/code&gt;&lt;/strong&gt; en esa misma poda, para tener la foto del consumo junto a la del panel de OmniRoute.&lt;/li&gt;&lt;/ol&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Las sesiones van por proyecto&lt;/p&gt;&lt;p&gt;OpenCode asocia las sesiones al directorio de trabajo desde el que se lanzó, así que &lt;code&gt;--continue&lt;/code&gt; o &lt;code&gt;-c&lt;/code&gt; retoma la última sesión &lt;em&gt;de ese proyecto&lt;/em&gt;, no la última en términos absolutos. Es un detalle que ayuda: trabajar siempre desde la raíz del repositorio mantiene los listados ordenados por proyecto de forma natural.&lt;/p&gt;&lt;/div&gt;&lt;h3 id=&#34;comandos-de-ponytail&#34;&gt;Comandos de Ponytail&lt;a class=&#34;headerlink&#34; href=&#34;#comandos-de-ponytail&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Más allá del nivel de intensidad, el &lt;em&gt;skill&lt;/em&gt; aporta un puñado de comandos que encajan en distintos momentos del flujo anterior:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Comando&lt;/th&gt;&lt;th&gt;Para qué sirve&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail [lite \| full \| ultra \| off]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Ajusta la intensidad o la desactiva. Sin argumento informa del nivel actual.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-review&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Revisa el &lt;em&gt;diff&lt;/em&gt; actual en busca de sobre-ingeniería y devuelve una lista de borrado.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-audit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Lo mismo, pero sobre todo el repositorio en lugar de sobre el &lt;em&gt;diff&lt;/em&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-debt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Recopila en un registro los atajos marcados con &lt;code&gt;ponytail:&lt;/code&gt; que se hayan ido aplazando.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-gain&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Muestra el marcador de impacto medido (menos código, menos coste, más velocidad).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-help&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Referencia rápida de los comandos anteriores.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Cuando el agente decide no construir algo, deja un comentario del tipo &lt;code&gt;ponytail: browser has one&lt;/code&gt; explicando el peldaño de la escalera en el que se detuvo. Esos comentarios son los que después recoge &lt;code&gt;/ponytail-debt&lt;/code&gt;, de forma que las decisiones de &#34;esto ya se hará si hace falta&#34; quedan registradas en lugar de perderse.&lt;/p&gt;&lt;h3 id=&#34;ajustes-segun-el-tipo-de-proyecto&#34;&gt;Ajustes según el tipo de proyecto&lt;a class=&#34;headerlink&#34; href=&#34;#ajustes-segun-el-tipo-de-proyecto&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Lo único que varía de forma apreciable entre escenarios es la elección de modelo y la preparación del contexto:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Proyectos nuevos desde cero (&lt;em&gt;greenfield&lt;/em&gt;)&lt;/strong&gt;. Hay poco contexto que cargar y mucho código que generar. Es el escenario ideal para las capas gratuitas: &lt;code&gt;auto/cheap&lt;/code&gt; o &lt;code&gt;auto/offline&lt;/code&gt; dan buen resultado y el consumo de tokens de entrada es bajo. Conviene empezar pidiendo el andamiaje del proyecto y el fichero de dependencias, y sólo después las funcionalidades. También es donde más se nota Ponytail, porque es donde el agente tiene más libertad para inventar estructura de más.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mantenimiento y evolución de código existente&lt;/strong&gt;. Aquí el cuello de botella es el contexto: el agente necesita leer bastante código antes de tocar nada. Interesa un &lt;code&gt;AGENTS.md&lt;/code&gt; detallado, activar la compresión de contexto de OmniRoute y usar &lt;code&gt;auto/coding&lt;/code&gt; para las tareas de modificación. Es también el caso donde más se nota el fallback, porque las sesiones son largas. El peldaño &#34;¿ya está en este repositorio?&#34; de Ponytail resulta especialmente valioso, ya que evita el clásico duplicado de un helper que ya existía tres directorios más allá.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Depuración de errores&lt;/strong&gt;. El flujo es el mismo, pero funciona mucho mejor si se le proporciona al agente una forma de reproducir el fallo (un test que falle, la traza completa, el comando exacto). Modelos con capacidad de razonamiento explícito dan mejores resultados; en OmniRoute se pueden seleccionar las variantes &lt;em&gt;thinking&lt;/em&gt; de los modelos que las ofrezcan. Un nivel &lt;code&gt;lite&lt;/code&gt; de Ponytail, o incluso &lt;code&gt;off&lt;/code&gt;, puede ser preferible aquí: al depurar interesa entender, no recortar.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scripting y automatización&lt;/strong&gt;. Aquí interesa el modo CLI no interactivo, que permite integrar el agente en scripts, &lt;em&gt;hooks&lt;/em&gt; de git o tareas programadas:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Actualiza el CHANGELOG con los commits desde la última etiqueta&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute/auto/fast&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Refactorizaciones amplias y repetitivas&lt;/strong&gt;. Trocear el trabajo en tareas independientes y lanzarlas por separado, en lugar de pedir un cambio masivo en una sola sesión. Además de dar mejores resultados, evita agotar la ventana de contexto y la cuota de un proveedor de una sentada. Si lo que se busca es precisamente adelgazar código heredado, &lt;code&gt;/ponytail-audit&lt;/code&gt; sobre el repositorio completo y &lt;code&gt;/ponytail ultra&lt;/code&gt; son el punto de partida natural.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Proyectos con código sensible&lt;/strong&gt;. Si no se puede enviar el código a terceros, la misma configuración sirve conectando en OmniRoute un proveedor local (Ollama o LM Studio en la propia máquina) en lugar de los servicios gratuitos. Cambia el proveedor conectado en el panel; ni OpenCode ni el flujo de trabajo se tocan.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusión&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;La combinación de OpenCode, OmniRoute y Ponytail proporciona un entorno de desarrollo agéntico completo con coste de entrada nulo y sin las interrupciones por cuota agotada que caracterizan a las alternativas comerciales. Cada pieza ataca un frente distinto: el agente aporta las capacidades, el gateway la cuota y la independencia de proveedor, y el &lt;em&gt;skill&lt;/em&gt; la disciplina para que el código generado sea el mínimo necesario. Y las tres se declaran en un único fichero de configuración de unas veinte líneas.&lt;/p&gt;&lt;p&gt;El precio a pagar es una configuración inicial algo más laboriosa que la de un producto cerrado y la necesidad de revisar de vez en cuando qué proveedores gratuitos siguen operativos. A cambio se obtiene algo que las suscripciones no dan: independencia respecto al proveedor, visibilidad completa de lo que consume el agente y menos código del que arrepentirse.&lt;/p&gt;</description><link>https://apuntes.eduardofilo.es/2026-07-26_opencode_omniroute.html</link> <pubDate>Sun, 26 Jul 2026 12:00:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/2026-07-26_opencode_omniroute.html</guid> </item> <item> <title>OpenCode, OmniRoute, and Ponytail Installation</title> <description>&lt;p&gt;&lt;img alt=&#34;OpenCode + OmniRoute&#34; src=&#34;../images/posts/2026-07-26_opencode_omniroute/opencode_omniroute.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;AI-assisted development began as intelligent line-level code completion. It evolved into a &#34;second opinion&#34; that reviewed file-level code and suggested changes. In the last two years, a major step forward has been taken to reach the current paradigm, &lt;em&gt;agentic development&lt;/em&gt;: an agent that works at the project-level, edits files, executes commands, performs changes that were previously only suggested, runs tests, and iterates until the task is complete. The problem is that all commercial tools of this type (Claude Code, Cursor, Devin, Copilot, Codex, etc.) share two limitations: a monthly subscription and quotas that run out just when the session is getting interesting. Because here we don&#39;t consider the use via API, which for the personal scope is unaffordable.&lt;/p&gt;&lt;p&gt;There is also a third, quieter but also expensive problem: agents tend to &lt;strong&gt;over-build&lt;/strong&gt;. They are asked for a date picker and end up installing a library, writing a wrapper component, and adding a stylesheet. Every extra line is paid for twice: in tokens when generating it and in tokens every time it re-enters the context.&lt;/p&gt;&lt;p&gt;This article describes how to set up a complete agentic development environment by combining three open-source projects that address these exact limitations:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://opencode.ai/&#34;&gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/a&gt;: the terminal-based programming agent.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://omniroute.online/&#34;&gt;&lt;strong&gt;OmniRoute&lt;/strong&gt;&lt;/a&gt;: the model router/gateway that powers the agent.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/DietrichGebert/ponytail&#34;&gt;&lt;strong&gt;Ponytail&lt;/strong&gt;&lt;/a&gt;: the &lt;em&gt;skill&lt;/em&gt; that enforces minimal code discipline on the agent.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;product-description&#34;&gt;Product Description&lt;a class=&#34;headerlink&#34; href=&#34;#product-description&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&#34;opencode&#34;&gt;OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OpenCode is an open-source programming agent that runs in the terminal (TUI) and, most importantly for our purpose, is &lt;strong&gt;model-provider agnostic&lt;/strong&gt;. Unlike Claude Code (tied to Anthropic) or Codex (tied to OpenAI), OpenCode allows configuring any provider, including those exposing an OpenAI-compatible API, which today is practically all of them.&lt;/p&gt;&lt;p&gt;Its main features:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Terminal interface with persistent sessions, context management, and &lt;em&gt;undo&lt;/em&gt; for applied changes.&lt;/li&gt;&lt;li&gt;Access to tools: file reading and writing, command execution, repository search, version control.&lt;/li&gt;&lt;li&gt;Per-project context file (&lt;code&gt;AGENTS.md&lt;/code&gt;) generated with &lt;code&gt;/init&lt;/code&gt; to instruct the agent on repository conventions.&lt;/li&gt;&lt;li&gt;MCP (&lt;em&gt;Model Context Protocol&lt;/em&gt;) support to add external tools.&lt;/li&gt;&lt;li&gt;Non-interactive CLI mode (&lt;code&gt;opencode run &#34;...&#34;&lt;/code&gt;) for automations and scripts.&lt;/li&gt;&lt;li&gt;Specialized agents and work modes (e.g., a &lt;em&gt;plan&lt;/em&gt; mode that makes no changes, designed for the pre-planning phase).&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;omniroute&#34;&gt;OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OmniRoute is a local, open-source AI &lt;em&gt;gateway&lt;/em&gt;. It is installed on your own machine (or a VPS), sets up an OpenAI-compatible API endpoint and a web administration panel, and from there routes each request to any of the hundreds of cataloged providers, about 90 of which have a free tier.&lt;/p&gt;&lt;p&gt;The interesting thing is not the catalog itself, but what it does with it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Automatic routing&lt;/strong&gt;: using the &lt;code&gt;auto&lt;/code&gt; virtual model, OmniRoute builds a &lt;em&gt;combo&lt;/em&gt; of your connected providers and chooses one for each request based on live scoring. Variants exist: &lt;code&gt;auto/coding&lt;/code&gt; (quality for code generation), &lt;code&gt;auto/fast&lt;/code&gt; (lowest latency), &lt;code&gt;auto/cheap&lt;/code&gt; (cheapest per token), and &lt;code&gt;auto/offline&lt;/code&gt; (most available quota margin).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Cascading fallback&lt;/strong&gt;: when a provider returns a quota error, rate limit, or 4xx-5xx, the request is transparently retried against the next provider in the list. This prevents the classic &#34;quota exceeded, come back in five hours&#34; message.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Context compression&lt;/strong&gt;: a set of engines that trim the prompt (deduplication, history pruning, file compacting), enabling significant token savings, especially valuable when working with free tiers measured in tokens/day.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Control panel&lt;/strong&gt; at &lt;code&gt;http://localhost:20128&lt;/code&gt; with per-provider usage stats, API key management, and assisted configuration for common CLI tools.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Local-first&lt;/strong&gt;: provider keys and traffic stay on your machine; OmniRoute is not a cloud service requiring a subscription.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&#34;ponytail&#34;&gt;Ponytail&lt;a class=&#34;headerlink&#34; href=&#34;#ponytail&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail is a &lt;strong&gt;&lt;em&gt;skill&lt;/em&gt;&lt;/strong&gt;, a set of instructions injected into the agent&#39;s context every turn to modify its behavior. Its sole mission is to combat over-engineering by forcing the agent to go through a decision ladder before writing a single line, stopping at the first rung that solves the problem:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;1. Does this need to exist? → no: don&amp;#39;t do it (YAGNI)2. Already in this codebase? → reuse it, don&amp;#39;t rewrite3. Stdlib does it? → use it4. Native platform feature? → use it5. Installed dependency? → use it6. One line? → one line7. Only then: the minimum that works&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The project&#39;s canonical example, which we referenced in the introduction, sums it up well: when asked for a date picker, the result is no longer a library plus a component but, thanks to Ponytail, &lt;code&gt;&amp;lt;input type=&#34;date&#34;&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Two important nuances:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The ladder is applied &lt;strong&gt;after&lt;/strong&gt; understanding the problem, not instead of it. The agent is still required to read the affected code and trace the actual flow before picking a rung. Lazy about the solution, never about reading.&lt;/li&gt;&lt;li&gt;Four things are never cut: trust-boundary validation, data-loss handling, security, and accessibility. The rule is not &#34;fewer tokens,&#34; it&#39;s &#34;only what the task needs.&#34;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;According to the project&#39;s published &lt;em&gt;benchmark&lt;/em&gt; (real agent sessions on a FastAPI + React repo, with and without the &lt;em&gt;skill&lt;/em&gt;), results vs. the baseline show 54% fewer lines of code, 22% fewer tokens, 20% lower cost, and 27% less time, with no penalty to security checks. As always with &lt;em&gt;benchmarks&lt;/em&gt;, take them as an order of magnitude rather than a promise: savings are huge where there&#39;s a clear over-build trap and near zero where code was already minimal.&lt;/p&gt;&lt;p&gt;Ponytail works with a good number of agents (Claude Code, Codex, Copilot CLI, Gemini CLI, Cursor, Devin...), and OpenCode is one of those with first-class &lt;em&gt;plugin&lt;/em&gt; support.&lt;/p&gt;&lt;h3 id=&#34;the-value-of-the-combination&#34;&gt;The Value of the Combination&lt;a class=&#34;headerlink&#34; href=&#34;#the-value-of-the-combination&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Separately each piece is useful, but combined, the limitations mentioned at the beginning vanish. Each covers a different layer and they don&#39;t overlap: OmniRoute decides &lt;strong&gt;who&lt;/strong&gt; responds, OpenCode decides &lt;strong&gt;what&lt;/strong&gt; is done, and Ponytail decides &lt;strong&gt;how much&lt;/strong&gt; code is written.&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Problem&lt;/th&gt;&lt;th&gt;How the combination solves it&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Monthly subscription&lt;/td&gt;&lt;td&gt;OpenCode is free and OmniRoute connects to dozens of providers&#39; free tiers. The entry cost is €0.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Quota exhaustion&lt;/td&gt;&lt;td&gt;OmniRoute&#39;s fallback jumps to the next provider without interrupting the agent&#39;s session.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Provider dependence&lt;/td&gt;&lt;td&gt;The agent only knows one endpoint (&lt;code&gt;localhost:20128/v1&lt;/code&gt;); changing models or providers requires no changes to the agent.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Excessive token consumption&lt;/td&gt;&lt;td&gt;OmniRoute&#39;s compression engines reduce the size of agent prompts, which are typically large due to repository context.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lack of spending visibility&lt;/td&gt;&lt;td&gt;The OmniRoute panel centralizes consumption across all providers and connected tools.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Oversized code&lt;/td&gt;&lt;td&gt;Ponytail trims what the agent builds, reducing code to review, maintain, and re-enter context in future sessions.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The combination of these three pieces has a multiplier effect on available quota: Ponytail reduces output tokens (less code generated), OmniRoute compression reduces input tokens (less context sent), and fallback utilizes every connected provider&#39;s quota.&lt;/p&gt;&lt;p&gt;An additional benefit: since OmniRoute exposes a standard endpoint, the same gateway simultaneously serves OpenCode in the terminal, a VSCode extension, or any other OpenAI-compatible client. Configure once, use everywhere.&lt;/p&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Modest models, better results&lt;/p&gt;&lt;p&gt;An interesting side effect of Ponytail is that it makes working with free (typically smaller) models more viable. The less code a model is asked to generate, the fewer chances it has to make a mistake; the &lt;em&gt;skill&lt;/em&gt; specifically reduces the size of the task in each step.&lt;/p&gt;&lt;/div&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;About Free Tiers&lt;/p&gt;&lt;p&gt;Provider free tier conditions change frequently and some disappear without notice. The advantage of the OmniRoute approach is that these changes are absorbed by the gateway (by connecting another provider) without touching the agent&#39;s configuration. It&#39;s also wise to review each free provider&#39;s privacy policy before sending them code from a sensitive project.&lt;/p&gt;&lt;/div&gt;&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;a class=&#34;headerlink&#34; href=&#34;#installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The entire procedure was performed on Linux. On Windows and MacOS, the steps are equivalent, only the method of installing requirements changes.&lt;/p&gt;&lt;p&gt;All three projects are distributed via npm, so the only common requirement is a recent version of Node.js (v22 or higher).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;node&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;opencode-installation&#34;&gt;OpenCode Installation&lt;a class=&#34;headerlink&#34; href=&#34;#opencode-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The recommended way is through the official install script:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-fsSL&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://opencode.ai/install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Alternatively, depending on the system:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-g&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode-ai&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# npm (also bun, pnpm, or yarn)&lt;/span&gt;brew&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;anomalyco/tap/opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# MacOS / Linux with Homebrew&lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Arch Linux (official repos)&lt;/span&gt;paru&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;opencode-bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Arch Linux (AUR, latest version)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Verify the installation by launching the agent in any directory:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;At this point, OpenCode will start but ask for provider credentials. No need to provide any yet: OmniRoute will fill that gap. For now, simply exit the program.&lt;/p&gt;&lt;h3 id=&#34;omniroute-installation&#34;&gt;OmniRoute Installation&lt;a class=&#34;headerlink&#34; href=&#34;#omniroute-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-g&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omnirouteomniroute&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;omniroute&lt;/code&gt; command starts both the API and the web panel on port &lt;code&gt;20128&lt;/code&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Panel: &lt;code&gt;http://localhost:20128&lt;/code&gt;&lt;/li&gt;&lt;li&gt;OpenAI-compatible API: &lt;code&gt;http://localhost:20128/v1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Other deployment methods exist, useful if you want to keep the gateway running permanently:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# container&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Besides the npm package and container, the project distributes a desktop app (Electron), arm64 images (works on a Raspberry Pi), and is executable on Android via Termux. An interesting option is to leave OmniRoute running on a VPS or home server and point all development machines at it.&lt;/p&gt;&lt;h3 id=&#34;ponytail-installation&#34;&gt;Ponytail Installation&lt;a class=&#34;headerlink&#34; href=&#34;#ponytail-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail isn&#39;t installed on the system; it&#39;s declared as an OpenCode &lt;em&gt;plugin&lt;/em&gt;. Simply add this entry to your &lt;code&gt;opencode.json&lt;/code&gt; configuration file (the next section shows the full file with this and the other pieces):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@dietrichgebert/ponytail&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;OpenCode downloads the package from npm the first time it starts with this configuration. If you prefer working with a local repo copy to modify rules, clone it and point to the &lt;em&gt;plugin&lt;/em&gt; file:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/DietrichGebert/ponytail.git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/ponytail&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;/home/user/ponytail/.opencode/plugins/ponytail.mjs&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Relative paths (like &lt;code&gt;./.opencode/plugins/ponytail.mjs&lt;/code&gt;) resolve relative to the &lt;code&gt;opencode.json&lt;/code&gt; declaring them; to share a single &lt;em&gt;checkout&lt;/em&gt; across projects, use the absolute path, as shown in the example.&lt;/p&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Works Without a Plugin Too&lt;/p&gt;&lt;p&gt;The repo includes rules in &lt;code&gt;AGENTS.md&lt;/code&gt; format and rule formats for Cursor, Devin, Cline, Kiro, Copilot, etc. Copying that file to your project yields the same always-on behavior in almost any agent. The OpenCode &lt;em&gt;plugin&lt;/em&gt; specifically adds the &lt;code&gt;/ponytail&lt;/code&gt; commands and intensity levels.&lt;/p&gt;&lt;/div&gt;&lt;h2 id=&#34;joint-configuration&#34;&gt;Joint Configuration&lt;a class=&#34;headerlink&#34; href=&#34;#joint-configuration&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The integration involves OpenCode stopping direct talk to providers to speak only to OmniRoute, while Ponytail is injected at every turn of the conversation. There are five steps.&lt;/p&gt;&lt;h3 id=&#34;1-connect-providers-in-omniroute&#34;&gt;1. Connect Providers in OmniRoute&lt;a class=&#34;headerlink&#34; href=&#34;#1-connect-providers-in-omniroute&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;With &lt;code&gt;omniroute&lt;/code&gt; running, open &lt;code&gt;http://localhost:20128&lt;/code&gt; and go to the &lt;strong&gt;Providers&lt;/strong&gt; section (I recommend setting the interface to &lt;code&gt;English&lt;/code&gt;, otherwise you&#39;ll see many strings prefixed with &lt;code&gt;__MISSING__&lt;/code&gt;; also use the search field, as the number of configuration groups in OmniRoute is huge). The catalog appears there with an indicator for free tiers. Connect &lt;strong&gt;several&lt;/strong&gt;, as the system&#39;s value lies in redundancy: when one runs out of quota, the others remain available. Providers requiring no key (&lt;code&gt;No Auth&lt;/code&gt;) are connected by default.&lt;/p&gt;&lt;p&gt;The catalog is classified by &lt;strong&gt;categories&lt;/strong&gt;, and the category determines the connection procedure:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Category&lt;/th&gt;&lt;th&gt;How to connect&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;No Auth&lt;/em&gt;&lt;/td&gt;&lt;td&gt;They are active from the start. Although we will deactivate some as we see later.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Free Tier&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Free tier that requires signing up with the provider and pasting an API key.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;OAuth&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Sign-in button for providers where you already have an account: OmniRoute opens the provider flow and saves the resulting &lt;em&gt;token&lt;/em&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;API Key&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Paid providers (some with initial free credits); paste the key.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Local&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Models running on your own machine (Ollama, LM Studio, vLLM…); specify the local URL.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The top filter allows you to narrow down to your interests, and the search bar accepts the provider&#39;s name or ID.&lt;/p&gt;&lt;h4 id=&#34;no-auth-providers&#34;&gt;No Auth Providers&lt;a class=&#34;headerlink&#34; href=&#34;#no-auth-providers&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In general, the selection and configuration of providers is the most complicated, cumbersome, and time-consuming part of the entire process described in this article.&lt;/p&gt;&lt;p&gt;Not only that, but some of the providers activated by default, such as those in the &#34;No Auth&#34; category, can cause problems. During the first sessions of using OpenCode, I observed that most requests returned empty responses. After investigating, I found that the issue was caused by the intervention of the &#34;Augment (Auggie CLI)&#34; provider, which I therefore recommend deactivating.&lt;/p&gt;&lt;p&gt;Additionally, checking the logs in the OmniRoute console (&lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt;), I found that the providers &#34;Chipotle Pepper AI (Free)&#34; and &#34;DuckDuckGo AI Chat&#34; always failed, so although they don&#39;t introduce incorrect responses like &#34;Augment (Auggie CLI)&#34;, they delay the provider cascade traversal, so I recommend deactivating them as well. We will deactivate the &#34;Veo AI Free&#34; provider, as it only offers video models that we won&#39;t use for coding.&lt;/p&gt;&lt;h4 id=&#34;free-api-key-providers&#34;&gt;Free API Key Providers&lt;a class=&#34;headerlink&#34; href=&#34;#free-api-key-providers&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The procedure is always the same, only the site for obtaining the key changes:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Sign up with the provider and generate an API key in their panel (the link is usually in the provider&#39;s own card in OmniRoute).&lt;/li&gt;&lt;li&gt;In OmniRoute, go to &lt;strong&gt;Providers&lt;/strong&gt; → search for the provider → click connect/configure.&lt;/li&gt;&lt;li&gt;Paste the key into the form and save.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Test&lt;/strong&gt; button on the provider card. If it responds correctly, its models join the gateway catalog and automatically enter the &lt;code&gt;auto&lt;/code&gt; model pool.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The providers in this category are those that will allow us to take full advantage of OmniRoute. They are numerous, but as mentioned in the previous section, it requires quite a bit of work to configure them (obtaining API keys from different providers) and to stay up to date with which ones change in operation or are more or less recommended at a given time.&lt;/p&gt;&lt;p&gt;Some documents to follow to keep the list of free providers in good shape are the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/wiki/Providers-Guide#best-free-providers&#34;&gt;Providers Guide - Best Free Providers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.49/docs/getting-started/FREE-TIERS-GUIDE.md&#34;&gt;FREE TIERS GUIDE&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.49/docs/reference/FREE_TIERS.md&#34;&gt;FREE TIERS&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Some of the providers in this type that I find recommendable at the time of writing this article are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://aistudio.google.com/api-keys&#34;&gt;Gemini (Google AI Studio)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://groq.com/&#34;&gt;Groq&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://console.mistral.ai/&#34;&gt;Mistral&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://dash.cloudflare.com/&#34;&gt;Cloudflare Workers AI&lt;/a&gt;: After entering the API Key, the attempt to connect to the models will fail, indicating that an &#34;Account ID&#34; is also required. The &#34;Account ID&#34; is the hash that appears below the URL &lt;code&gt;https://dash.cloudflare.com&lt;/code&gt; once the session is created and started. We will enter that hash in the &#34;Account ID&#34; field of the provider settings.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://enter.pollinations.ai/&#34;&gt;Pollinations AI&lt;/a&gt;: Although the connection box indicates that the API Key is optional, it is better to create one.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;oauth-providers&#34;&gt;OAuth Providers&lt;a class=&#34;headerlink&#34; href=&#34;#oauth-providers&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;With these, there&#39;s no key to copy: click the connection button, sign in through the provider&#39;s browser flow, and OmniRoute will save encrypted credentials in its local database. This applies to existing tool accounts (Copilot, Cursor, Kilo Code, Qoder, etc.).&lt;/p&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Review Terms of Service&lt;/p&gt;&lt;p&gt;Several of these providers are services designed for their own clients; some expressly prohibit use via proxies or third-party clients. The OmniRoute provider card includes a warning when this is the case. It&#39;s wise to read these notices before connecting an account you want to keep.&lt;/p&gt;&lt;/div&gt;&lt;h4 id=&#34;curating-the-pool-for-agentic-use&#34;&gt;Curating the Pool for Agentic Use&lt;a class=&#34;headerlink&#34; href=&#34;#curating-the-pool-for-agentic-use&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;There is one issue that isn&#39;t mentioned in the OmniRoute documentation and that, in my experience, has been essential to getting the setup up and running. The point is that &lt;strong&gt;not all vendors in the catalog are suitable for assigning to an agent&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The reason is that an agent like OpenCode doesn&#39;t just ask for text: every request carries the list of &lt;em&gt;tools&lt;/em&gt; at its disposal (read files, run commands, search the repository…) and it expects the model to reply with calls to those tools. Providers that are the official API of a model return those calls to the client, which is the correct behaviour. But the catalog also includes two kinds of entries that behave differently:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Proxies of a web client&lt;/strong&gt; (those you connect to with a chat session cookie rather than an API key). The service on the other end has its own tool engine and tries to execute the tools itself, in its own environment, instead of returning them.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Agent front-ends&lt;/strong&gt; (services designed to be consumed from their own command line tool). They ship their own tool catalog and don&#39;t fit as the engine behind another agent.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The symptom is baffling, because the agent reports no error at all: it simply returns an empty response, or a text in which the model explains that it could not explore the repository and asks us, for example, to paste the contents of the files ourselves.&lt;/p&gt;&lt;h5 id=&#34;an-admission-test&#34;&gt;An Admission Test&lt;a class=&#34;headerlink&#34; href=&#34;#an-admission-test&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;The reliable way to decide whether a provider belongs in the pool is to ask the gateway: send a request with a tool and check whether the model invokes it. The response must contain a &lt;code&gt;tool_calls&lt;/code&gt; block with the tool name and its arguments; if prose arrives instead, that provider is no good.&lt;/p&gt;&lt;p&gt;A single request against &lt;code&gt;auto/coding&lt;/code&gt; is not enough to validate the pool. &lt;code&gt;auto&lt;/code&gt; routing tends to stick to the last working provider and does not guarantee that all candidates receive traffic. Each provider must be tested separately.&lt;/p&gt;&lt;p&gt;The problematic behavior we want to detect (proxies that execute tools themselves, front-ends with their own tool catalog) is a property &lt;strong&gt;of the provider&lt;/strong&gt;, not of the individual model: a proxy either passes &lt;code&gt;tool_calls&lt;/code&gt; back to the client or it doesn&#39;t, regardless of which model sits behind it. That is why it is enough to test &lt;strong&gt;one representative model per provider&lt;/strong&gt;, identified by the prefix before the slash (&lt;code&gt;groq/&lt;/code&gt;, &lt;code&gt;mistral/&lt;/code&gt;, &lt;code&gt;cf/&lt;/code&gt;...), instead of the hundreds of models in the catalog. In reality we will test the first 5 models of each provider in case one fails for some specific reason. The &lt;code&gt;jq&lt;/code&gt; filter excludes only the virtual &lt;code&gt;auto/*&lt;/code&gt; models:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;36&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;38&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;39&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;40&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nv&#34;&gt;API&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-your-omniroute-key&amp;quot;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# List of providers (excluding auto/*)&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;providers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/models&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[.data[].id | select(startswith(&amp;quot;auto/&amp;quot;) | not)]&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; | group_by(split(&amp;quot;/&amp;quot;)[0]) | map(.[0]) | .[]&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cut&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-f1&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sort&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-u&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;prov&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$providers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Up to 5 models from this provider&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;prov_models&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/models&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-r&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--arg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;p&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[.data[].id | select(startswith($p+&amp;quot;/&amp;quot;))][:5][]&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tested&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov_models&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;tested&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$model&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-s&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-i&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$API&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/chat/completions&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$KEY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-d&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;{\&amp;quot;model\&amp;quot;:\&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$model&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;\&amp;quot;,\&amp;quot;stream\&amp;quot;:false,\&amp;quot;messages\&amp;quot;:[{\&amp;quot;role\&amp;quot;:\&amp;quot;user\&amp;quot;,\&amp;quot;content\&amp;quot;:\&amp;quot;Read the file /etc/hostname\&amp;quot;}],\&amp;quot;tools\&amp;quot;:[{\&amp;quot;type\&amp;quot;:\&amp;quot;function\&amp;quot;,\&amp;quot;function\&amp;quot;:{\&amp;quot;name\&amp;quot;:\&amp;quot;read\&amp;quot;,\&amp;quot;description\&amp;quot;:\&amp;quot;Reads a file\&amp;quot;,\&amp;quot;parameters\&amp;quot;:{\&amp;quot;type\&amp;quot;:\&amp;quot;object\&amp;quot;,\&amp;quot;properties\&amp;quot;:{\&amp;quot;path\&amp;quot;:{\&amp;quot;type\&amp;quot;:\&amp;quot;string\&amp;quot;}},\&amp;quot;required\&amp;quot;:[\&amp;quot;path\&amp;quot;]}}}]}&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;http_code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$resp&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;head&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-1&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;grep&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-o&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;[0-9][0-9][0-9]&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$resp&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sed&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/^\r\?$/,$p&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;tail&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+2&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;200&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$body&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;jq&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-e&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.choices[0].message.tool_calls[0].function.name&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;/dev/null&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FAIL (no tool_calls)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;401&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;403&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FAIL (auth &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$http_code&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# 404, 400... model unavailable, try the next one&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-z&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;FAIL (no valid models)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;%-24s %-45s %s\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$status&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prov&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$tested&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Requires &lt;code&gt;jq&lt;/code&gt;. The script distinguishes three failure causes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FAIL (no tool_calls)&lt;/code&gt;&lt;/strong&gt;: the provider returned a 200 but with prose instead of a tool call. This is the problem we are after: a proxy that runs the tools itself or a front-end with its own catalog. =&amp;gt; Disconnect it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FAIL (auth 401/403)&lt;/code&gt;&lt;/strong&gt;: the provider&#39;s key is expired, misconfigured, or out of quota. This is not a &lt;em&gt;tools&lt;/em&gt; problem but a configuration one. =&amp;gt; Fix the connection in &lt;strong&gt;Providers&lt;/strong&gt; (or disconnect it if there&#39;s no remedy).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;FAIL (no valid models)&lt;/code&gt;&lt;/strong&gt;: every model tested returned 404 or 400. The provider is down or its catalog has changed. =&amp;gt; Review it in &lt;strong&gt;Providers&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the output, the right-hand column shows the model that produced the result. This is a one-time setup step that you only need to repeat when adding a new provider.&lt;/p&gt;&lt;h5 id=&#34;two-symptoms-to-spot-in-the-logs&#34;&gt;Two Symptoms to Spot in the Logs&lt;a class=&#34;headerlink&#34; href=&#34;#two-symptoms-to-spot-in-the-logs&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;In &lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt; there are two patterns that give away a provider worth retiring:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;200&lt;/code&gt; with &lt;code&gt;TO: 0&lt;/code&gt;&lt;/strong&gt; in a few milliseconds: the provider returned an empty response with a success status. A real inference never takes 15 ms.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Slow responses whose content is text&lt;/strong&gt; where &lt;code&gt;tool_calls&lt;/code&gt; should be, often carrying error messages from the remote service itself explaining that the tools don&#39;t exist.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Fallback doesn&#39;t trigger on failures disguised as success&lt;/p&gt;&lt;p&gt;It&#39;s worth understanding the limits of the system. OmniRoute&#39;s retry cascade works with &lt;strong&gt;explicit&lt;/strong&gt; failures: a 429 for quota, a 502 or a timeout cause the request to be retried against the next provider, and this is perfectly visible in the log. But the two cases above reach the gateway as correct responses: a &lt;code&gt;200&lt;/code&gt; with an empty body is, to a router, a valid response. No gateway can arbitrate that without semantically inspecting the content of every response.&lt;/p&gt;&lt;p&gt;Hence curating the pool is our responsibility, and it&#39;s a step that shouldn&#39;t be skipped. Put another way: &lt;strong&gt;automatic routing is only as good as the worst apparently healthy provider in the pool&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt;&lt;h3 id=&#34;2-create-an-omniroute-api-key&#34;&gt;2. Create an OmniRoute API Key&lt;a class=&#34;headerlink&#34; href=&#34;#2-create-an-omniroute-api-key&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In the panel, go to &lt;strong&gt;API Manager&lt;/strong&gt; → &lt;strong&gt;Create API Key&lt;/strong&gt;. Give it a descriptive name (e.g., &lt;code&gt;opencode&lt;/code&gt;) and copy the generated key, which looks like &lt;code&gt;sk-xxxxxxxx-xxxxxxxx&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;This is a local key: it&#39;s used by your clients (OpenCode in this case) to authenticate against OmniRoute and has nothing to do with provider keys, which are held by OmniRoute.&lt;/p&gt;&lt;p&gt;You can verify the gateway is responding and see available models:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;http://localhost:20128/v1/models&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-H&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Authorization: Bearer sk-your-omniroute-key&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;3-declare-omniroute-as-a-provider-in-opencode&#34;&gt;3. Declare OmniRoute as a Provider in OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#3-declare-omniroute-as-a-provider-in-opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;OpenCode supports any OpenAI-compatible provider by declaring it in its config file. To apply configuration globally, write it to &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;$schema&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;https://opencode.ai/config.json&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;plugin&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@dietrichgebert/ponytail&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;provider&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;omniroute&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;npm&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;@ai-sdk/openai-compatible&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;OmniRoute&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;options&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;baseURL&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;apiKey&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-your-omniroute-key&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;models&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (balanced)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/coding&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (coding quality)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/cheap&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (cheapest)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/fast&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (fastest)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;auto/offline&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Auto (max quota)&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;quot;model&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;omniroute/auto/coding&amp;quot;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Important points of this configuration:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;npm&lt;/code&gt;: the &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt; adapter is used for &lt;code&gt;/v1/chat/completions&lt;/code&gt; endpoints, which OmniRoute uses.&lt;/li&gt;&lt;li&gt;&lt;code&gt;options.baseURL&lt;/code&gt;: the gateway endpoint. If OmniRoute is on another machine, use its IP or domain here.&lt;/li&gt;&lt;li&gt;&lt;code&gt;options.apiKey&lt;/code&gt;: the API key created in OmniRoute.&lt;/li&gt;&lt;li&gt;&lt;code&gt;models&lt;/code&gt;: no need to list specific provider models; simply declare the &lt;code&gt;auto&lt;/code&gt; virtual models to enable smart routing and fallback. To fix a specific model, add it using the ID returned by &lt;code&gt;/v1/models&lt;/code&gt; (see &lt;a href=&#34;#2-create-an-omniroute-api-key&#34;&gt;previous point&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;&lt;code&gt;model&lt;/code&gt;: default model at startup. &lt;code&gt;auto/coding&lt;/code&gt; is a good choice for development; &lt;code&gt;auto/offline&lt;/code&gt; is useful when quota headroom is the priority.&lt;/li&gt;&lt;li&gt;&lt;code&gt;plugin&lt;/code&gt;: the list of OpenCode &lt;em&gt;plugins&lt;/em&gt;, where Ponytail belongs.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Key Outside the Config File&lt;/p&gt;&lt;p&gt;If the config file will be versioned (e.g., in a &lt;em&gt;dotfiles&lt;/em&gt; repo), it&#39;s better not to write the key in it. OpenCode allows storing credentials with &lt;code&gt;/connect&lt;/code&gt; (choose &lt;em&gt;Other&lt;/em&gt;, ID &lt;code&gt;omniroute&lt;/code&gt;), in which case you can omit &lt;code&gt;options.apiKey&lt;/code&gt; from the JSON.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;You can also place an &lt;code&gt;opencode.json&lt;/code&gt; file in a specific project&#39;s root, merging its settings with the global ones. This is practical for forcing a different model for a specific project.&lt;/p&gt;&lt;h3 id=&#34;4-adjust-ponytail-level&#34;&gt;4. Adjust Ponytail Level&lt;a class=&#34;headerlink&#34; href=&#34;#4-adjust-ponytail-level&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ponytail needs no config file: it works as soon as it&#39;s declared as a &lt;em&gt;plugin&lt;/em&gt;. The only thing to decide is the &lt;strong&gt;intensity level&lt;/strong&gt;, which can be changed on the fly from the TUI:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/ponytail → reports current level/ponytail lite → mild trimming/ponytail full → default level/ponytail ultra → aggressive trimming/ponytail off → disabled for this session&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The default level is &lt;code&gt;full&lt;/code&gt;. To set a different default for all new sessions, use the &lt;code&gt;PONYTAIL_DEFAULT_MODE&lt;/code&gt; environment variable or the &lt;code&gt;defaultMode&lt;/code&gt; field in &lt;code&gt;~/.config/ponytail/config.json&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PONYTAIL_DEFAULT_MODE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;full&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;While active, the ruleset is also injected into subagents spawned by the main agent. To exclude some (e.g., read-only search agents), use a regex against the subagent type defined in the following environment variable:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;PONYTAIL_SUBAGENT_MATCHER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;build|code&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h3 id=&#34;5-verify-integration&#34;&gt;5. Verify Integration&lt;a class=&#34;headerlink&#34; href=&#34;#5-verify-integration&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Start the agent and verify the provider appears:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Within the TUI, the &lt;code&gt;/models&lt;/code&gt; command should show OmniRoute entries.&lt;/p&gt;&lt;p&gt;From a directory containing a repository or project, run a quick test in CLI non-interactive mode:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Summarize this repo in three lines&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute/auto/coding&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;While it runs, the OmniRoute panel (&lt;code&gt;Monitoring &amp;gt; Logs&lt;/code&gt;) shows in real-time which provider handled the request, tokens consumed, and compression savings. If a provider fails, the log shows the automatic jump to the next.&lt;/p&gt;&lt;p&gt;To check if Ponytail is loaded, simply run &lt;code&gt;/ponytail&lt;/code&gt; in the TUI (it returns the active level) or &lt;code&gt;/ponytail-help&lt;/code&gt; (lists its commands).&lt;/p&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Apparent Ponytail Startup ERROR&lt;/p&gt;&lt;p&gt;If OpenCode is started with &lt;code&gt;--print-logs&lt;/code&gt;, this line may appear when loading the configuration:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ERROR message=&amp;quot;failed to load plugin&amp;quot; path=@dietrichgebert/ponytail error=&amp;quot;path must be a string or a file descriptor&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;It&#39;s misleading: OpenCode tries to load the plugin by two paths and one of them fails, but the other loads it correctly. The test that Ponytail is operational is that &lt;code&gt;/ponytail&lt;/code&gt; responds with the active level. Note that the level is &lt;strong&gt;persisted&lt;/strong&gt; between sessions (in &lt;code&gt;~/.config/opencode/.ponytail-active&lt;/code&gt;): a &lt;code&gt;/ponytail off&lt;/code&gt; from a previous test leaves the plugin loaded but disabled. When in doubt: &lt;code&gt;/ponytail full&lt;/code&gt; and ask the agent what its &lt;em&gt;system prompt&lt;/em&gt; says about &lt;code&gt;PONYTAIL&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Environment Variables for Other Tools&lt;/p&gt;&lt;p&gt;Exporting these variables in your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; makes any other OpenAI-convention-respecting tool use the gateway without further configuration:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;http://localhost:20128/v1&amp;quot;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;sk-your-omniroute-key&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&#34;project-types-and-operations&#34;&gt;Project Types and Operations&lt;a class=&#34;headerlink&#34; href=&#34;#project-types-and-operations&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once the environment is set up, the workflow is essentially the same regardless of language, project size, or task nature. What changes between cases is not the procedure, but the choice of model, trimming level, and context preparation. Thus, we first describe the common flow and then the adjustments per project type.&lt;/p&gt;&lt;h3 id=&#34;common-operations&#34;&gt;Common Operations&lt;a class=&#34;headerlink&#34; href=&#34;#common-operations&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Navigate to the repository and start the agent&lt;/strong&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;my-projectopencode&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generate the context file&lt;/strong&gt; the first time using the &lt;code&gt;/init&lt;/code&gt; command in the TUI. OpenCode explores the repo and writes an &lt;code&gt;AGENTS.md&lt;/code&gt; with the project description, build/test commands, and detected conventions. It&#39;s worth reviewing and manual completion; it&#39;s the most influential document for result quality and should be versioned with the project.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plan before executing&lt;/strong&gt;. For any non-trivial change, ask for a plan first rather than direct implementation. OpenCode has a planning mode that doesn&#39;t write to disk; reviewing a plan costs much less than reviewing a 400-line incorrect &lt;em&gt;diff&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterate in small steps&lt;/strong&gt;, validating with your project tools (compiler, &lt;em&gt;linter&lt;/em&gt;, tests) after each step. The agent can execute these commands itself if documented in &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pass the Ponytail review&lt;/strong&gt; before finalizing the task with &lt;code&gt;/ponytail-review&lt;/code&gt;. It analyzes the current &lt;em&gt;diff&lt;/em&gt; for over-engineering and returns a delete-list. This step quickly pays off in saved time.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review the &lt;code&gt;diff&lt;/code&gt; and commit&lt;/strong&gt; using version control. Always work on a branch and make small &lt;em&gt;commits&lt;/em&gt;: it&#39;s the natural safety net when an agent writes the code.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clear context between tasks&lt;/strong&gt; with &lt;code&gt;/new&lt;/code&gt;. Dragging context from a finished task worsens results and spikes token consumption. The next section details session management.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor the OmniRoute panel&lt;/strong&gt; occasionally to see which providers are in use, which are exhausted, and if compression savings are as expected.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&#34;session-management-in-opencode&#34;&gt;Session Management in OpenCode&lt;a class=&#34;headerlink&#34; href=&#34;#session-management-in-opencode&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;When starting with OpenCode, it&#39;s surprising to find that sessions remain listed even after exiting the TUI with &lt;code&gt;Ctrl-d&lt;/code&gt; or &lt;code&gt;/exit&lt;/code&gt;. It&#39;s not a hang or incomplete closure: &lt;strong&gt;a session is not a process, it&#39;s a persisted conversation&lt;/strong&gt;. Exiting the TUI ends the interface, but the conversation history (messages, tools, files, token usage) is stored in OpenCode&#39;s local database for later resumption. Since each &lt;code&gt;opencode&lt;/code&gt; call without arguments starts a new session, you&#39;ll have dozens of entries in a few days.&lt;/p&gt;&lt;p&gt;In other words: &#34;closing&#34; doesn&#39;t mean &#34;deleting.&#34; You must build the habit of reusing and pruning.&lt;/p&gt;&lt;h4 id=&#34;resume-instead-of-create&#34;&gt;Resume Instead of Create&lt;a class=&#34;headerlink&#34; href=&#34;#resume-instead-of-create&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;First, stop starting from scratch. Here are three ways to continue previous work:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--continue&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# resumes the last session of the current project (-c)&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# resumes a specific session (-s)&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--fork&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# forks it into a copy, leaving the original intact&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;From within the TUI, the &lt;code&gt;/sessions&lt;/code&gt; command (aliases: &lt;code&gt;/resume&lt;/code&gt;, &lt;code&gt;/continue&lt;/code&gt;; shortcut: &lt;code&gt;Ctrl+x l&lt;/code&gt;) opens a selector to switch sessions without exiting.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;--fork&lt;/code&gt; option is particularly useful when a conversation reaches an interesting point and you want to test two different paths: it forks, and each branch goes its own way, much like a &lt;code&gt;git branch&lt;/code&gt;.&lt;/p&gt;&lt;h4 id=&#34;closing-tasks-properly&#34;&gt;Closing Tasks Properly&lt;a class=&#34;headerlink&#34; href=&#34;#closing-tasks-properly&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Two TUI commands mark the end of a task:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;/new&lt;/code&gt; (alias: &lt;code&gt;/clear&lt;/code&gt;, shortcut: &lt;code&gt;Ctrl+x n&lt;/code&gt;): opens a new, clean session. Use this when switching tasks instead of continuing in the old conversation.&lt;/li&gt;&lt;li&gt;&lt;code&gt;/compact&lt;/code&gt; (alias: &lt;code&gt;/summarize&lt;/code&gt;, shortcut: &lt;code&gt;Ctrl+x c&lt;/code&gt;): summarizes the current conversation to free up the context window without losing the thread. Use this if the same task is getting too &#34;heavy&#34; context-wise.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To keep a session&#39;s result, &lt;code&gt;/export&lt;/code&gt; dumps the conversation to Markdown and opens your editor, while &lt;code&gt;opencode export &amp;lt;sessionID&amp;gt;&lt;/code&gt; does the same in JSON from the CLI.&lt;/p&gt;&lt;h4 id=&#34;inventory-and-pruning&#34;&gt;Inventory and Pruning&lt;a class=&#34;headerlink&#34; href=&#34;#inventory-and-pruning&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;From outside the TUI, you manage the complete inventory:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# list sessions&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-n&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# limit number of entries&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;list&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--format&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;json&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# script-processable output&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;session&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;delete&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;lt;sessionID&amp;gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# delete a session&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;To see cumulative cost, which in this setup determines how much free quota you&#39;re burning:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# token usage and cost&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--days&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--models&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# last 7 days, per-model breakdown&lt;/span&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;stats&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--project&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# current project only&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;h4 id=&#34;workflow&#34;&gt;Workflow&lt;a class=&#34;headerlink&#34; href=&#34;#workflow&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Putting it all together, the routine to keep the list under control is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;One session per task&lt;/strong&gt;, not per day or project. The natural unit is whatever ends in a &lt;em&gt;commit&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Resume with &lt;code&gt;-c&lt;/code&gt;&lt;/strong&gt; when returning to a partial task, instead of starting &lt;code&gt;opencode&lt;/code&gt; fresh and re-explaining context (which costs tokens).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/new&lt;/code&gt; when switching tasks&lt;/strong&gt; and &lt;code&gt;/compact&lt;/code&gt; if the task grows but remains the same.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Export what&#39;s worth keeping&lt;/strong&gt; before closing a session with valuable conclusions.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Periodic pruning&lt;/strong&gt; (e.g., weekly), reviewing &lt;code&gt;opencode session list&lt;/code&gt; and deleting anything already committed that adds no value with &lt;code&gt;opencode session delete&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Review &lt;code&gt;opencode stats&lt;/code&gt;&lt;/strong&gt; during the same prune to get the consumption picture alongside the OmniRoute panel.&lt;/li&gt;&lt;/ol&gt;&lt;div class=&#34;admonition tip&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Sessions Are Per Project&lt;/p&gt;&lt;p&gt;OpenCode associates sessions with the working directory they were launched from; thus, &lt;code&gt;--continue&lt;/code&gt; or &lt;code&gt;-c&lt;/code&gt; resumes the last session &lt;em&gt;of that project&lt;/em&gt;, not the last overall. This helps: always working from the repo root naturally keeps lists project-ordered.&lt;/p&gt;&lt;/div&gt;&lt;h3 id=&#34;ponytail-commands&#34;&gt;Ponytail Commands&lt;a class=&#34;headerlink&#34; href=&#34;#ponytail-commands&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Beyond intensity levels, the &lt;em&gt;skill&lt;/em&gt; provides a few commands fitting different workflow stages:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail [lite \| full \| ultra \| off]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Adjusts intensity or disables it. Reports current level if no argument is provided.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-review&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Reviews the current &lt;em&gt;diff&lt;/em&gt; for over-engineering and returns a delete-list.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-audit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same, but for the entire repo instead of just the &lt;em&gt;diff&lt;/em&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-debt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Harvests &lt;code&gt;ponytail:&lt;/code&gt; shortcuts deferred into a ledger.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-gain&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shows the measured impact scoreboard (less code/cost, more speed).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/ponytail-help&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Quick reference for the above commands.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;When the agent decides not to build something, it leaves a comment like &lt;code&gt;ponytail: browser has one&lt;/code&gt;, explaining the ladder rung where it stopped. These comments are what &lt;code&gt;/ponytail-debt&lt;/code&gt; harvests, so &#34;defer for now&#34; decisions are recorded rather than lost.&lt;/p&gt;&lt;h3 id=&#34;adjustments-by-project-type&#34;&gt;Adjustments by Project Type&lt;a class=&#34;headerlink&#34; href=&#34;#adjustments-by-project-type&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The only significant variation between scenarios is model choice and context preparation:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;New projects from scratch (&lt;em&gt;greenfield&lt;/em&gt;)&lt;/strong&gt;. Little context to load and lots of code to generate. Ideal for free tiers: &lt;code&gt;auto/cheap&lt;/code&gt; or &lt;code&gt;auto/offline&lt;/code&gt; yield good results and input token consumption is low. Start by asking for project scaffolding and dependency files, then functionalities. Ponytail shines here, as agents have more freedom to invent extra structure.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintenance and evolution of existing code&lt;/strong&gt;. Context is the bottleneck here; the agent needs to read significant code before touching anything. A detailed &lt;code&gt;AGENTS.md&lt;/code&gt;, active OmniRoute context compression, and &lt;code&gt;auto/coding&lt;/code&gt; for modifications are key. Fallback is most noticeable here due to long sessions. Ponytail&#39;s &#34;Already in this codebase?&#34; rung is especially valuable, preventing duplicate helpers.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error debugging&lt;/strong&gt;. The flow is the same but works much better if you provide a way to reproduce the failure (failing test, full trace, exact command). Explicit reasoning models yield better results; choose &lt;em&gt;thinking&lt;/em&gt; variants in OmniRoute. A &lt;code&gt;lite&lt;/code&gt; or &lt;code&gt;off&lt;/code&gt; Ponytail level might be preferable here: debugging is about understanding, not trimming.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scripting and automation&lt;/strong&gt;. CLI non-interactive mode is useful here for integration into scripts, git hooks, or scheduled tasks:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opencode&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;run&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;quot;Update CHANGELOG with commits since the last tag&amp;quot;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--model&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;omniroute/auto/fast&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large, repetitive refactors&lt;/strong&gt;. Break work into independent tasks and launch them separately instead of one massive change. This yields better results and avoids exhausting context windows or provider quotas in one go. If slimming down legacy code is the goal, &lt;code&gt;/ponytail-audit&lt;/code&gt; on the whole repo and &lt;code&gt;/ponytail ultra&lt;/code&gt; are the natural starting point.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sensitive code projects&lt;/strong&gt;. If code cannot be sent to third parties, the same setup works by connecting a local provider in OmniRoute (Ollama, LM Studio) instead of free services. Only the provider in the panel changes; OpenCode and the workflow remain the same.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The combination of OpenCode, OmniRoute, and Ponytail provides a complete agentic development environment with zero entry cost and no quota interruptions. Each piece attacks a different front: the agent provides capabilities, the gateway provides quota and provider independence, and the &lt;em&gt;skill&lt;/em&gt; provides discipline for minimal code generation. All three are declared in a single 20-line config file.&lt;/p&gt;&lt;p&gt;The price is a slightly more laborious initial setup compared to closed products and the need to occasionally check active free providers. In return, you get what subscriptions don&#39;t offer: provider independence, full consumption visibility, and less code to regret.&lt;/p&gt;</description><link>https://apuntes.eduardofilo.es/en/2026-07-26_opencode_omniroute.html</link> <pubDate>Sun, 26 Jul 2026 12:00:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/en/2026-07-26_opencode_omniroute.html</guid> </item> <item> <title>G&amp;W Zelda hack</title> <description>&lt;p&gt;&lt;img alt=&#34;Zelda G&amp;amp;W&#34; src=&#34;images/posts/2026-03-08_zelda_gw/zelda_gnw.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Un conocido fabricante japonés de videojuegos empezó a comercializar a finales de 2022, un par de máquinas replicando el aspecto de algunas de las viejas G&amp;amp;W, sobre la base de un microcontrolador STM32H7 que ejecuta un firmware baremetal con emuladores de NES y GB, tres juegos clásicos y una aplicación reloj inspirados en la franquicia Zelda. La máquina resulta muy interesante por las virtudes de estar basada en un microcontrolador, en lugar de un microprocesador, es decir gran duración de la pequeña batería que contiene (la de un joycon de Switch), arranque y modo suspensión instantáneos. El soporte de datos es un pequeño chip flash NOR de 4MB (en el caso del modelo Zelda), suficiente para alojar los emuladores y juegos mencionados, pero muy escaso si se quiere sacar partido de la máquina y de su relativamente potente CPU.&lt;/p&gt;&lt;p&gt;El firmware original sirve para comprobar la solvencia de la plataforma para emular al menos las máquinas de 8 bit que funcionan originalmente, por lo que en seguida el dispositivo resultó atractivo para la comunidad hacker. Al poco tiempo de empezar a comercializarse ya &lt;a href=&#34;https://www.youtube.com/watch?v=Rsi8p5gbaps&#34;&gt;aparecieron procedimientos para desbloquear y modificar la máquina&lt;/a&gt; de manera que se pudieran instalar más emuladores y juegos.&lt;/p&gt;&lt;p&gt;La primera modificación importante propuesta por la comunidad consistía en sustituir el chip flash por otro de 64MB para disponer de más espacio para juegos y emuladores. Tiempo después la modificación evolucionó para incluir un slot de tarjetas microSD donde almacenar los juegos y savestates, así como para simplificar enormemente el proceso de actualización, convirtiendo a la máquina así modificada en una máquina de emulación portátil inigualable en aspectos como portabilidad, tamaño, duración de la batería y tiempos de arranque.&lt;/p&gt;&lt;p&gt;Con cualquiera de las dos modificaciones se consigue emular las siguientes máquinas:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Amstrad CPC6128 (beta)&lt;/li&gt;&lt;li&gt;Atari 2600&lt;/li&gt;&lt;li&gt;Atari 7800&lt;/li&gt;&lt;li&gt;ColecoVision&lt;/li&gt;&lt;li&gt;Gameboy / Gameboy Color&lt;/li&gt;&lt;li&gt;Game &amp;amp; Watch / LCD Games&lt;/li&gt;&lt;li&gt;MSX1/2/2+&lt;/li&gt;&lt;li&gt;Nintendo Entertainment System&lt;/li&gt;&lt;li&gt;PC Engine / TurboGrafx-16&lt;/li&gt;&lt;li&gt;Pokémon Mini&lt;/li&gt;&lt;li&gt;Sega Game Gear&lt;/li&gt;&lt;li&gt;Sega Genesis / Megadrive&lt;/li&gt;&lt;li&gt;Sega Master System&lt;/li&gt;&lt;li&gt;Sega SG-1000&lt;/li&gt;&lt;li&gt;Tamagotchi P1&lt;/li&gt;&lt;li&gt;Watara Supervision&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;En este artículo vamos a describir los pasos para realizar las dos modificaciones. La modificación para añadir el slot microSD requiere también la sustitución de la flash, ya que cada vez que carguemos un juego nuevo de la microSD se grabará en el chip flash, y el original de 4MB soporta pocos ciclos de escritura dado que no estaba previsto que se utilizara de esta forma.&lt;/p&gt;&lt;p&gt;Todo el proceso ha sido realizado sobre un sistema Linux. En principio el desarrollo sobre sistemas Windows y MacOS X también es posible, pero los pasos para instalar los requerimientos y el software que utilizaremos necesariamente cambiarán.&lt;/p&gt;&lt;h2 id=&#34;conexion-al-puerto-swd&#34;&gt;Conexión al puerto SWD&lt;a class=&#34;headerlink&#34; href=&#34;#conexion-al-puerto-swd&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;El desbloqueo del procesador y la modificación del firmware se hacen a través del puerto SWD que habitualmente tienen los microcontroladores STM32 para programar su memoria flash interna y depurar los desarrollos. Este puerto SWD se encuentra en la PCB sin poblar, es decir sin pines o conectores soldados.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;SWD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/swd.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Para hacer la conexión con dicho puerto hay varias opciones:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Soldar cables directamente.&lt;/li&gt;&lt;li&gt;Introducir cables limpios o cables de pin macho, inclinándolos para forzar el contacto.&lt;/li&gt;&lt;li&gt;Utilizar &lt;a href=&#34;https://www.amazon.es/gp/product/B009UOHE1K/&#34;&gt;pinzas sonda de conexión&lt;/a&gt; especializadas.&lt;/li&gt;&lt;li&gt;Utilizar &lt;a href=&#34;https://es.aliexpress.com/item/1005007602295920.html&#34;&gt;pinzas pogo pin&lt;/a&gt; (Single row 5pin).&lt;/li&gt;&lt;li&gt;Rutear los pines SWDIO y SWCLK hacia el puerto USB-C.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;version-dificil&#34;&gt;Versión difícil&lt;a class=&#34;headerlink&#34; href=&#34;#version-dificil&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Como empecé a cacharrear con la máquina antes de que apareciera el mod de microSD, en su momento realicé el ruteado del puerto SWD al USB-C. Si se va a terminar realizando el mod de microSD no merece la pena el esfuerzo, dado que a partir de entonces el flasheo del firmware se realizará desde la propia microSD. De todas formas, a propósitos de documentación vamos a describir cómo se haría el ruteado.&lt;/p&gt;&lt;p&gt;El puerto USB-C sólo tiene conectados los pines de alimentación ya que está previsto únicamente para cargar la máquina. Por tanto se pueden utilizar los pines D+ y D- que están libres para cablearlos a los pines SWDIO y SWCLK que son los únicos imprescindibles del puerto SWD. Necesitaremos también conectar a 5V y GND, pero estos ya se encuentran cableados en el puerto USB-C. El ruteado se hace con un fino cable barnizado (enamel) de los que se utilizan para construir bobinas. La soldadura a los dos pines centrales del puerto USB-C que son los que corresponden a D+ y D- es complicada al tener un paso muy pequeño, pero con paciencia y flux puede lograrse.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;images/posts/2026-03-08_zelda_gw/ruteado_usb-c_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Una vez resuelto la única parte difícil del procedimiento, es decir soldar los cables al puerto USB, estos se van sujetando cada cierta distancia con cinta Kapton y dirigiendo hacia la zona de la batería por donde se aprovecha el rebaje a ambos lados que hay del alojamiento que la rodea para pasar uno de los cables. El otro se conduce por el espacio que hay entre alojamiento de la batería y la PCB. De esta forma se consigue que ambos cables no puedan tocarse, aunque en teoría al estar barnizados no debería haber problema, se adopta este camino separado como precaución adicional.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;images/posts/2026-03-08_zelda_gw/ruteado_usb-c_2.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Una vez que los cables alcanzan las proximidades del puerto SWD, se utilizan los dos test pads que pueden verse en la foto para realizar las soldaduras.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;images/posts/2026-03-08_zelda_gw/ruteado_usb-c_3.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;La correspondencia entre D+ y D- en el puerto USB-C con los SWDIO y SWCLK del puerto SWD, puede hacerse de cualquier forma siempre que tengamos claro donde está cada cual para más tarde hacer el adaptador USB-SWD que necesitaremos. En este montaje se ha optado por la siguiente correspondencia.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;D+ ↔ SWDIO&lt;/li&gt;&lt;li&gt;D- ↔ SWCLK&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Warning&lt;/p&gt;&lt;p&gt;La idea en un principio era adoptar la correspondencia utilizada en &lt;a href=&#34;https://facelesstech.wordpress.com/2022/01/08/game-and-watch-hacking-with-rpi/&#34;&gt;esta otra guía&lt;/a&gt;, pero por un error cometido al principio, se terminó haciendo justo al revés. Como se comentaba antes, en realidad no es problema siempre que se tenga claro cómo están hechas las conexiones. Este aviso es sólo por si alguien intenta seguir ambas guías de forma simultánea, para que se tenga en cuenta dicha diferencia.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Una vez hecho el ruteado del puerto, necesitaremos construir el adaptador para poder extraer las señales SWDIO y SWCLK del puerto USB-C. Para ello se corta un cable USB-A convencional para tener acceso a los cuatro cables que contiene por separado y se sueldan a ellos conectores hembra de pin. El adaptador se completa con un adaptador USB-A a USB-C que se marca para orientarlo correctamente cuando lo vayamos a utilizar, ya que sólo hemos ruteado los pines D+ y D- de uno de los dos lados (el puerto USB-C es reversible).&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Adaptador USB-C SWD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/adaptador_usb-c_swd.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Los colores de los cables suelen estar estandarizados de la siguiente forma:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Rojo: 5V&lt;/li&gt;&lt;li&gt;Negro: GND&lt;/li&gt;&lt;li&gt;Verde: D+&lt;/li&gt;&lt;li&gt;Blanco: D-&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;version-facil&#34;&gt;Versión fácil&lt;a class=&#34;headerlink&#34; href=&#34;#version-facil&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;La manera recomendada para hacer la conexión al puerto SWD (si no se quiere soldar, que es la opción más fiable) es mediante &lt;a href=&#34;https://www.amazon.es/gp/product/B009UOHE1K/&#34;&gt;pinzas sonda de conexión&lt;/a&gt; especializadas. Para aplicar dichas pinzas, es recomendable desconectar previamente la batería de la consola. El conector de la batería salta fácilmente al introducir una pieza fina de plástico por debajo del mismo.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Pinzas conexión GnW&#34; src=&#34;images/posts/2026-03-08_zelda_gw/pinzas_conexion_gnw.jpg&#34; /&gt;&lt;/p&gt;&lt;h2 id=&#34;sonda-de-programacion&#34;&gt;Sonda de programación&lt;a class=&#34;headerlink&#34; href=&#34;#sonda-de-programacion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Ya tenemos resuelto dónde conectar en la consola. Ahora necesitamos un dispositivo capaz de comunicarse con ese puerto SWD del microcontrolador. Las opciones son:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.raspberrypi.com/products/raspberry-pi-pico/&#34;&gt;Raspberry Pi Pico&lt;/a&gt; (Recomendado)&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.st.com/en/development-tools/st-link-v2.html&#34;&gt;STLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.segger.com/products/debug-probes/j-link/#models&#34;&gt;JLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://daplink.io/&#34;&gt;DAPLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://projects.raspberrypi.org/en/projects/physical-computing/1&#34;&gt;Raspberry Pi (GPIO)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Me decanté por usar una Raspberry Pi Pico. Para que funcione como sonda de programación SWD hay que cargarle el firmware &lt;a href=&#34;https://github.com/raspberrypi/debugprobe&#34;&gt;debugprobe&lt;/a&gt;. En mi caso utilicé el fichero &lt;a href=&#34;https://github.com/raspberrypi/debugprobe/releases/download/picoprobe-cmsis-v1.0.3/picoprobe.uf2&#34;&gt;picoprobe.uf2&lt;/a&gt; de &lt;a href=&#34;https://github.com/raspberrypi/debugprobe/releases/tag/picoprobe-cmsis-v1.0.3&#34;&gt;esta release&lt;/a&gt; en concreto. Para flashear la Pico sólo hay que conectarla al ordenador por USB mientras pulsamos el botón &lt;code&gt;BOOTSEL&lt;/code&gt; para forzar el modo en el que se monta como una unidad de almacenamiento y arrastrar el fichero &lt;code&gt;.uf2&lt;/code&gt; a esa unidad. Tras autoflashearse, la Pico se reiniciará automáticamente y empezará a funcionar como sonda de programación SWD.&lt;/p&gt;&lt;p&gt;Una vez programada la Pico, solo queda hacer las conexiones de los cables que habíamos conectado al puerto SWD de la Game&amp;amp;Watch. Podemos utilizar el mismo tipo de pinzas que aplicamos a la consola, aunque en mi caso utilicé otras similares pero un poco más robustas, ya que el espaciado de los pines GPIO de la Pico es mucho mayor.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Pinzas conexión Pico&#34; src=&#34;images/posts/2026-03-08_zelda_gw/pinzas_conexion_pico.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;El conexionado entre Game&amp;amp;Watch y Raspberry Pi Pico es:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Pico&lt;/th&gt;&lt;th style=&#34;text-align: left;&#34;&gt;GnW&lt;/th&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Color&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;3&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;3&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Negro&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;4&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;5&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Blanco&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;5&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;2&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Verde&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;img alt=&#34;GnW - Pico&#34; src=&#34;images/posts/2026-03-08_zelda_gw/gnw_pico.png&#34; /&gt;&lt;/p&gt;&lt;h2 id=&#34;desbloqueo-del-microcontrolador&#34;&gt;Desbloqueo del microcontrolador&lt;a class=&#34;headerlink&#34; href=&#34;#desbloqueo-del-microcontrolador&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Una vez resuelta la conectividad a la placa, el primer paso en la modificación es el desbloqueo del microcontrolador STM32H7 para que podamos sustituir el firmware que hay en su memoria flash interna así como poder escribir en la memoria flash externa (con código que inyectaremos en el propio microcontrolador).&lt;/p&gt;&lt;p&gt;Esta tarea se efectúa con el conjunto de utilidades &lt;a href=&#34;https://github.com/BrianPugh/gnwmanager&#34;&gt;gnwmanager&lt;/a&gt;. Este paquete se encuentra registrado en &lt;a href=&#34;https://pypi.org/project/gnwmanager/&#34;&gt;PyPI&lt;/a&gt; por lo que se puede instalar muy fácilmente con &lt;code&gt;pip&lt;/code&gt; (preferiblemente dentro de un entorno virtual Python).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pip&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openocd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Ahora, tras volver a conectar la batería de la consola si la habíamos desconectado, ejecutaremos el siguiente comando:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(gnwmanager) $ gnwmanager unlock&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;El proceso se interrumpirá en un par de ocasiones dando instrucciones por la terminal que deberemos seguir. Una salida típica sería:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(gnwmanager) [edu@thinkpad ~]$ gnwmanager unlockIf interrupted, resume unlocking with: gnwmanager unlock --backup-dir=backups-2025-10-27-19-28-14Detected ZELDA game and watch.Backing up itcm to &amp;quot;backups-2025-10-27-19-28-14/itcm_backup_zelda.bin&amp;quot;... complete!Backing up external flash to &amp;quot;backups-2025-10-27-19-28-14/flash_backup_zelda.bin&amp;quot;... complete!Flashing payload to external flash... complete!Payload successfully flashed. Perform the following steps:1. Fully remove power, then re-apply power.2. Press the power button to turn on the device; the screen should turn blue.Press the &amp;quot;enter&amp;quot; key when the screen is blue: Backing up internal flash to &amp;quot;backups-2025-10-27-19-28-14/internal_flash_backup_zelda.bin&amp;quot;... complete!Unlocking device... complete!Restoring firmware... complete!Unlocking complete!Pressing the power button should launch the original firmware.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Aparecerán los ficheros &lt;code&gt;internal_flash_backup_zelda.bin&lt;/code&gt;, &lt;code&gt;flash_backup_zelda.bin&lt;/code&gt; y &lt;code&gt;itcm_backup_zelda.bin&lt;/code&gt; en un directorio &lt;code&gt;backups-FECHA-HORA&lt;/code&gt; que conviene guardar.&lt;/p&gt;&lt;p&gt;Si encendemos en este momento la consola, no notaremos ningún cambio, es decir ejecutará el firmware original, pero ahora el chip estará desprotegido y podremos flashearle otro firmware.&lt;/p&gt;&lt;h2 id=&#34;modificacion-1-retro-go&#34;&gt;Modificación 1: retro-go&lt;a class=&#34;headerlink&#34; href=&#34;#modificacion-1-retro-go&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h4 id=&#34;sustitucion-de-la-flash&#34;&gt;Sustitución de la flash&lt;a class=&#34;headerlink&#34; href=&#34;#sustitucion-de-la-flash&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;En este momento toca empezar a modificar la consola seriamente. Como se comentaba al principio, el chip de almacenamiento externo del microcontrolador es una memoria flash de solo 4MB (modelo Zelda). No solo es pequeño para poder cargar muchos juegos, sino que tiene pocos ciclos de escritura, por lo que sustituir los juegos frecuentemente, o cachearlos cuando se hace la modificación para añadir la microSD, puede desgastarlo rápidamente. La modificación habitual es desoldar el chip flash de 4MB original y sustituirlo por otro de 64MB, concretamente el modelo &lt;a href=&#34;https://es.aliexpress.com/item/1005005138227815.html&#34;&gt;MX25U51245GZ4I00&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Para retirar el chip flash original, es muy recomendable extraer la placa de la consola, desconectando las dos fajas de datos de la pantalla, desoldando el altavoz y retirando todos los tornillos. Una vez que tengamos la placa sobre la mesa de trabajo, protegemos con cinta kapton los alrededores del chip y lo extraemos aplicando flux y aire caliente con una pistola.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 1&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_flash_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 2&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_flash_2.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;A continuación limpiamos con malla desoldadora la huella del chip que acabamos de retirar. También, aunque hay que hacerlo con sumo cuidado, ayuda rascar (con la punta de las pinzas por ejemplo) la máscara de soldadura de la PCB para ampliar hacia el exterior la superficie soldable de la huella de soldadura del chip.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 3&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_flash_3.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;El nuevo chip flash tiene formato QFN-8 de soldadura superficial que no resulta sencillo de soldar. Es recomendable aplicar flux y preestañar los pads de dicho chip por la parte inferior. También recomiendo poner una tira de cinta kapton en la parte inferior de manera que este no quede directamente apoyado sobre la PCB, lo que dificultaría el flujo de estaño por los pads.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Nuevo chip flash&#34; src=&#34;images/posts/2026-03-08_zelda_gw/new_flash.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Hay que fijarse bien en el punto que lleva el chip en su encapsulado que marca el pin número 1 que se corresponde con el que tiene un triángulo indicador en la PCB como se puede ver en la segunda foto de las siguientes.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 4&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_flash_4.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 5&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_flash_5.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;flasheo-retro-go&#34;&gt;Flasheo retro-go&lt;a class=&#34;headerlink&#34; href=&#34;#flasheo-retro-go&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Una vez instalado el chip, es hora de flashear el firmware. Para esta modificación, me decanto por no hacer dual boot, es decir no instalar el firmware original parcheado junto a retro-go, para así tener todo el espacio de la memoria flash dedicado a los juegos. Para ello empezamos bajando el repositorio del mismo:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/gitgit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--recursive&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/sylverb/game-and-watch-retro-go&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-b&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;filesystem_wip&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;game-and-watch-retro-go&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Una vez bajado el código, toca instalar las ROMs en los directorios correspondientes a las distintas máquinas soportadas que encontraremos en &lt;code&gt;~/git/game-and-watch-retro-go/roms/&lt;/code&gt;. Por ejemplo, para instalar las ROMs de NES, copiamos las ROMs a &lt;code&gt;~/git/game-and-watch-retro-go/roms/nes/&lt;/code&gt;. Si queremos portadillas para los juegos, situaremos en los mismos directorios y con los mismos nombres de fichero de las ROMs correspondientes, las imágenes en formato jpg o png. Por ejemplo, para la ROM &lt;code&gt;zelda.nes&lt;/code&gt;, situaremos la portada en &lt;code&gt;~/git/game-and-watch-retro-go/roms/nes/zelda.jpg&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Tenemos que tener en cuenta que todas las ROMs y sus portadillas tienen que caber en el chip flash de 64 MB que hemos instalado a la consola. La mayoría de las ROMs se comprimen, por lo que como regla de partida, utilizaremos el criterio de elegir ROMs por un tamaño total de aproximadamente el doble (128MB).&lt;/p&gt;&lt;p&gt;Ahora toca compilar el firmware (si no vamos a utilizar portadillas para aprovechar el espacio de sus ficheros para poder poner más ROMs, no incluir las variables de entorno &lt;code&gt;COVERFLOW&lt;/code&gt; y &lt;code&gt;JPG_QUALITY&lt;/code&gt; en los comandos de compilación y flasheo):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker_buildmake&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openocd&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;GNW_TARGET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;zelda&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;ADAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;cmsis-dap&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clean&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-j&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Al terminar la compilación se nos informará del tamaño resultante del binario que se flasheará en el chip. Por ejemplo:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;External flash usage Capacity: 61050880 Bytes ( 58.223 MB) Usage: 54335345 Bytes ( 51.818 MB) Free: 6715535 Bytes ( 6.404 MB)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Con esa información a la vista podremos iterar el proceso añadiendo o retirando ROMs hasta obtener un binario de unos 55MB, ya que hay que prever algo para los savestates de los juegos.&lt;/p&gt;&lt;p&gt;Una vez que estemos satisfechos con la ocupación del espacio, flashearemos el firmware volviendo a conectar la sonda de programación que comentamos en el apartado &lt;a href=&#34;#sonda-de-programacion&#34;&gt;Sonda de programación&lt;/a&gt;.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Una salida típica sería:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker@53335bdd9199:/opt/workdir$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_gnwmanagerEntering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;LCD-Game-Emulator&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blueMSX-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;caprice32-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;fceumm-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;gwenesis&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;potator&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;prosystem-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;retro-go-stm32&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;smw&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tamalib&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;zelda3&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;BASH&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Checking&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;updated&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;roms&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;█████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;208&lt;/span&gt;/208&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;04&lt;/span&gt;:20&amp;lt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:00,&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;.25s/it&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Es importante que guardemos el fichero &lt;code&gt;build/gw_retro_go.elf&lt;/code&gt; porque lo necesitaremos para extraer los savestates en el futuro.&lt;/p&gt;&lt;h4 id=&#34;respaldar-savestates-retro-go&#34;&gt;Respaldar savestates retro-go&lt;a class=&#34;headerlink&#34; href=&#34;#respaldar-savestates-retro-go&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Si más adelante queremos extraer los savestates de los juegos, para poder volverlos a instalar tras flashear un nuevo firmware, en primer lugar necesitaremos depositar en el directorio &lt;code&gt;build&lt;/code&gt; el fichero &lt;code&gt;gw_retro_go.elf&lt;/code&gt; que obtuvimos en el paso anterior. Después podremos arrancar de nuevo el entorno docker y ejecutar el script de backup:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/git/game-and-watch-retro-gomake&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;GNW_TARGET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;zelda&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;ADAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;cmsis-dap&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./scripts/saves_backup.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;build/gw_retro_go.elf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Los savestates aparecerán en el subdirectorio &lt;code&gt;~/git/game-and-watch-retro-go/save_states&lt;/code&gt;. Una vez que hayamos compilado y flasheado un nuevo firmware, podremos restaurarlos ejecutando el script de restauración:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./scripts/saves_restore.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;La restauración se hace de forma inteligente, es decir, si tenemos savestates de un juego que no está en el nuevo firmware, dicho savestate no se instalará en la consola, para evitar desperdiciar espacio de la memoria flash.&lt;/p&gt;&lt;h2 id=&#34;modificacion-2-retro-go-sd&#34;&gt;Modificación 2: retro-go-sd&lt;a class=&#34;headerlink&#34; href=&#34;#modificacion-2-retro-go-sd&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h4 id=&#34;instalacion-modulo-slot-microsd&#34;&gt;Instalación módulo slot microSD&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-modulo-slot-microsd&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Al igual que con la modificación para el retro-go normal, para utilizar retro-go-sd hay que empezar haciendo una modificación hardware a la consola. Esta vez es más complicada, sobre todo la fase final de soldadura del módulo directamente sobre las patitas del microcontrolador, que tienen un paso muy pequeño.&lt;/p&gt;&lt;p&gt;El módulo se compone del propio lector de tarjetas microSD y 4 componentes. Todo ello soldado sobre una PCB tipo flex. La lista de componentes completa es:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005008902154552.html&#34;&gt;lector de tarjetas microSD&lt;/a&gt;&lt;/li&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005005677654015.html&#34;&gt;resistencia 100kΩ 0603&lt;/a&gt; o 0805&lt;/li&gt;&lt;li&gt;2 &lt;a href=&#34;https://es.aliexpress.com/item/1005003512666695.html&#34;&gt;condensadores de 1μF 0603&lt;/a&gt; o 0805&lt;/li&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005007136710461.html&#34;&gt;regulador de tensión RT9193-28GB&lt;/a&gt;. Hay personas que han reportado problemas con este modelo comprado en Aliexpress y recomiendan como reemplazo el &#34;MIC5504-2.8YM5-TR&#34;.&lt;/li&gt;&lt;li&gt;1 PCB flex: Aquí se utiliza la versión de &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/raw/refs/heads/main/assets/MicroSD_Zelda_Final.zip&#34;&gt;PrimoAngelo&lt;/a&gt;. La original que suele referenciarse en la mayoría de las guías es la de &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/raw/refs/heads/main/assets/GnW_SD_v2.zip&#34;&gt;Tim Schuerewegen / hundshamer&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Empezaremos soldando los componentes sobre el PCB flex.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Módulo microSD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_msd_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Lo siguiente como decíamos al principio será el difícil paso de instalar el módulo en la consola. Retiramos los cuatro tornillos que caen en su superficie y lo presentamos de forma que consigamos que los pads de la flex se encuentren alineados con las patitas del microcontrolador a la vez que estos quedan a sus pies, es decir de esta forma.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Módulo microSD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_msd_2.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Una vez que se encuentre alineado a nuestro gusto, procederemos a fijarlo en su posición atornillando los cuatro tornillos. Ahora será cuando tendremos que armarnos de paciencia y flux de buena calidad para lograr hacer las soldaduras de los pads del flex y evitar a la vez los puentes entre las mismas. Será necesario comprobar continuidad de las soldaduras así como la no existencia de puentes entre las patitas adyacentes. Si finalmente lo conseguimos, solo resta añadir un punto de soldadura entre el pad marcado con &lt;code&gt;+4V&lt;/code&gt; y el terminal del condensador que queda a su lado.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Módulo microSD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/mod_msd_3.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;flasheo-bootloader&#34;&gt;Flasheo bootloader&lt;a class=&#34;headerlink&#34; href=&#34;#flasheo-bootloader&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Lo siguiente será flashear el bootloader junto al firmware original parcheado de la consola para permitir el dual boot (esta vez sí merece la pena al contar con espacio prácticamente ilimitado para ROMs en la microSD). Desde el directorio donde estén los ficheros de backup de la consola y con un entorno Python que tenga instalado &lt;code&gt;gnwmanager&lt;/code&gt;, ejecutaremos el siguiente comando:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;gnwmanager&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash-patch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;zelda&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;internal_flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--bootloader&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Una salida típica sería:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;gnwmanager&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash-patch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;zelda&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;internal_flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--bootloader&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;███████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;/16&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:58&amp;lt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:00,&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;.66s/it&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Si encendemos en ese momento la consola, tras haber retirado la sonda de programación, veremos que arranca el firmware original (parcheado). Tras pulsar &lt;code&gt;GAME + LEFT&lt;/code&gt; se ejecutará el bootloader y, como en ese momento Retro-Go SD todavía no estará instalado en la flash interna del STM32, se nos muestrará el siguiente aviso:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Aviso del bootloader&#34; src=&#34;images/posts/2026-03-08_zelda_gw/bootloader_warning.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;instalacion-de-retro-go-sd&#34;&gt;Instalación de Retro-Go SD&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-de-retro-go-sd&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Solo nos restará instalar el firmware de Retro-Go SD. Para ello, copiaremos el fichero &lt;code&gt;retro-go_update.bin&lt;/code&gt; que descargaremos de la última release publicada por Sylverb en el &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/releases&#34;&gt;repositorio de Retro-Go SD&lt;/a&gt; al directorio raíz de la microSD. Al arrancar por primera vez, veremos en pantalla cómo se copian los ficheros de retro-go y los cores de los emuladores a la misma microSD.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Instalación de Retro-Go SD&#34; src=&#34;images/posts/2026-03-08_zelda_gw/retro-go_update.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;A la vez se creará en la microSD la estructura de directorios siguiente:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;retro-go/├── CONFIG├── bios/│ ├── coleco/│ ├── msx/│ ├── nes/├── cores/│ ├── mappers/│ ├── a2600.bin│ ├── a2600_defprops.bin│ ├── a7800.bin│ └── ...├── data/│ ├── a2600/│ ├── a7800/│ ├── amstrad/│ └── ...├── fonts/│ ├── cp1251_greybeard.bin│ ├── cp1251_sans_serif.bin│ ├── cp1251_sans_serif_bold.bin│ └── ...└── roms/ ├── a2600/ ├── a7800/ ├── amstrad/ └── ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Sólo restará copiar las ROMs que queramos emular en los directorios correspondientes. Si queremos portadillas para los juegos, tendremos que crear el subdirectorio &lt;code&gt;covers&lt;/code&gt; con los mismos subdirectorios del directorio &lt;code&gt;roms&lt;/code&gt; y colocar allí las imágenes. Estas tienen que estar en un formato especial IMG que generaremos con un &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/blob/main/README.md#cover-art-generator-gencoverspy&#34;&gt;script que se incluye en el repositorio de Retro-Go SD&lt;/a&gt;. Los nombres de los archivos IMG tendrán que coincidir con los de las ROMs en su correspondiente directorio.&lt;/p&gt;&lt;h4 id=&#34;carcasa-trasera&#34;&gt;Carcasa trasera&lt;a class=&#34;headerlink&#34; href=&#34;#carcasa-trasera&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Al colocar el slot microSD hay que modificar la tapa trasera de la consola puesto que el lector cae sobre unos postes de apoyo de la cruceta. También hay que perforar el lateral para dejar paso a la microSD para poder extraerla e insertarla cuando queramos cambiar los juegos, rescatar savestates o actualizar el firmware. Para realizar la perforación &lt;a href=&#34;https://www.printables.com/model/1269910-zelda-game-and-watch-sd-card-drill-jig/files&#34;&gt;existen unos utillajes&lt;/a&gt; diseñados por el usuario facelesstech que ayudan mucho a que la ranura quede limpia. En el vídeo que hay al final de este artículo, puede verse cómo se realiza esta modificación.&lt;/p&gt;&lt;p&gt;Existe una alternativa si no queremos arriesgarnos a estropear definitivamente la tapa original, y es imprimir en 3D el modelo creado por el usuario Aradia y que puede descargarse de &lt;a href=&#34;files/posts/2026-03-08_zelda_gw/G%26W%20Zelda%20-%20Coque%20arri%C3%A8re%20-%20Mod%20Retro%20Go%20SD%20-%20v1.stl&#34;&gt;aquí&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Carcasa trasera&#34; src=&#34;images/posts/2026-03-08_zelda_gw/carcasa.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;El color verde que he encontrado más parecido al de la consola Zelda es el &lt;a href=&#34;https://www.amazon.es/dp/B0DHKZCL75&#34;&gt;15-Verde de OVERTURE&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;controles&#34;&gt;Controles&lt;a class=&#34;headerlink&#34; href=&#34;#controles&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Indicamos a continuación las combinaciones de teclas de manejo de Retro-go SD (aunque muchas de ellas funcionan también en Retro-go). Manteniendo pulsado el botón &lt;code&gt;PAUSE/SET&lt;/code&gt; mientras se presionan otros botones se realizan las siguientes acciones:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Combinación de teclas&lt;/th&gt;&lt;th&gt;Acción&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;GAME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Guardar una captura de pantalla (deshabilitado por defecto en builds con flash de 1MB)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;TIME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cambiar velocidad de ejecución (1x/1.5x)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;UP&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Aumentar brillo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;DOWN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Disminuir brillo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;RIGHT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Aumentar volumen&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;LEFT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Disminuir volumen&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cargar state&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Guardar state&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Autodisparo en botón A&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Autodisparo en botón B&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;POWER&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Apagar sin guardar savestate&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;POWER&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Después de haber hecho &lt;code&gt;PAUSE/SET&lt;/code&gt; y seleccionar &lt;code&gt;Power off&lt;/code&gt;, arrancará el bootloader sin llegar a llamar a Retro-go, lo que nos permitirá ver la versión del bootloader así como información sobre la detección de la microSD y el estado de las memorias flash interna y externa&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;GAME&lt;/code&gt; + &lt;code&gt;LEFT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Desde el OFW, hace que arranque el bootloader sin terminar de cargar retro-go. Sirve para ver la pantalla que muestra la detección de SD, chip flash y código en bank2 de flash interna; también para ver la versión del bootloader.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GAME&lt;/code&gt; + menú &lt;code&gt;Reset settings&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Borra los datos de registro en la SD de lo que hay cacheado en la flash; utilizar cuando se cambie la SD de máquina o se cambie el chip flash. Es equivalente a borrar el fichero &lt;code&gt;flashcachedata.bin&lt;/code&gt; en el directorio &lt;code&gt;data&lt;/code&gt; de la SD.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&#34;referencias&#34;&gt;Referencias&lt;a class=&#34;headerlink&#34; href=&#34;#referencias&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;No quiero terminar sin hacer mención al servidor de Discord de &lt;a href=&#34;https://discord.gg/TKjHZ5yV&#34;&gt;stacksmashing&lt;/a&gt; y a los siguientes usuarios cuyo trabajo ha hecho posible estas modificaciones:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;stacksmashing&lt;/li&gt;&lt;li&gt;kbeckmann&lt;/li&gt;&lt;li&gt;Tim Schuerewegen&lt;/li&gt;&lt;li&gt;BrianPugh&lt;/li&gt;&lt;li&gt;GMMan&lt;/li&gt;&lt;li&gt;tfmoe__&lt;/li&gt;&lt;li&gt;Sylver Bruneau&lt;/li&gt;&lt;li&gt;facelesstech&lt;/li&gt;&lt;li&gt;DasBoss&lt;/li&gt;&lt;li&gt;bxhxx&lt;/li&gt;&lt;li&gt;Benjamin Solberg&lt;/li&gt;&lt;li&gt;orzeus&lt;/li&gt;&lt;li&gt;ducalex&lt;/li&gt;&lt;li&gt;ZimM&lt;/li&gt;&lt;li&gt;Ninoh-FOX&lt;/li&gt;&lt;li&gt;PrimoAngelo&lt;/li&gt;&lt;li&gt;Aradia&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;En el siguiente video de Macho Nacho se puede ver el todo el proceso de modificación de la consola (el flex del módulo microSD es el modelo que no se sujeta con los tornillos sino con puntos de soldadura sobre la PCB de la G&amp;amp;W):&lt;/p&gt;&lt;iframe width=&#34;688&#34; height=&#34;387&#34; src=&#34;https://www.youtube.com/embed/8YIHjUM8Qms&#34; title=&#34;Making The Zelda G&amp;W Into A MODERN Emulation Powerhouse&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;</description><link>https://apuntes.eduardofilo.es/2026-03-08_zelda_gw.html</link> <pubDate>Sun, 08 Mar 2026 15:45:00 +0100</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/2026-03-08_zelda_gw.html</guid> </item> <item> <title>G&amp;W Zelda hack</title> <description>&lt;p&gt;&lt;img alt=&#34;Zelda G&amp;amp;W&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/zelda_gnw.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;A well-known Japanese video game manufacturer began selling a pair of machines at the end of 2022, replicating the appearance of some of the old G&amp;amp;W, based on an STM32H7 microcontroller running a baremetal firmware with NES and GB emulators, three classic games and a clock application inspired by the Zelda franchise. The machine is very interesting because of the virtues of being based on a microcontroller instead of a microprocessor, that is, great battery life (the one from a Switch joycon), instant boot and sleep mode. The storage medium is a small 4MB NOR flash chip (in the case of the Zelda model), enough to host the mentioned emulators and games, but very scarce if you want to take advantage of the machine and its relatively powerful CPU.&lt;/p&gt;&lt;p&gt;The original firmware serves to verify the solvency of the platform to emulate at least the 8-bit machines that originally run, so the device immediately became attractive to the hacker community. Shortly after it went on sale, &lt;a href=&#34;https://www.youtube.com/watch?v=Rsi8p5gbaps&#34;&gt;procedures appeared to unlock and modify the machine&lt;/a&gt; so that more emulators and games could be installed.&lt;/p&gt;&lt;p&gt;The first major modification proposed by the community consisted of replacing the flash chip with a 64MB one to have more space for games and emulators. Later, the modification evolved to include a microSD card slot to store games and savestates, as well as to greatly simplify the update process, turning the modified machine into an unparalleled portable emulation machine in aspects such as portability, size, battery life, and boot times.&lt;/p&gt;&lt;p&gt;With either of the two modifications, it is possible to emulate the following machines:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Amstrad CPC6128 (beta)&lt;/li&gt;&lt;li&gt;Atari 2600&lt;/li&gt;&lt;li&gt;Atari 7800&lt;/li&gt;&lt;li&gt;ColecoVision&lt;/li&gt;&lt;li&gt;Gameboy / Gameboy Color&lt;/li&gt;&lt;li&gt;Game &amp;amp; Watch / LCD Games&lt;/li&gt;&lt;li&gt;MSX1/2/2+&lt;/li&gt;&lt;li&gt;Nintendo Entertainment System&lt;/li&gt;&lt;li&gt;PC Engine / TurboGrafx-16&lt;/li&gt;&lt;li&gt;Pokémon Mini&lt;/li&gt;&lt;li&gt;Sega Game Gear&lt;/li&gt;&lt;li&gt;Sega Genesis / Megadrive&lt;/li&gt;&lt;li&gt;Sega Master System&lt;/li&gt;&lt;li&gt;Sega SG-1000&lt;/li&gt;&lt;li&gt;Tamagotchi P1&lt;/li&gt;&lt;li&gt;Watara Supervision&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In this article we will describe the steps to perform these two modifications. The modification to add the microSD slot also requires replacing the flash, since every time we load a new game from the microSD it will be written to the flash chip, and the original 4MB one supports few write cycles since it was not intended to be used this way.&lt;/p&gt;&lt;p&gt;The entire process has been performed on a Linux system. In principle, development on Windows and MacOS X systems is also possible, but the steps to install the requirements and the software we will use will necessarily change.&lt;/p&gt;&lt;h2 id=&#34;connection-to-the-swd-port&#34;&gt;Connection to the SWD port&lt;a class=&#34;headerlink&#34; href=&#34;#connection-to-the-swd-port&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Unlocking the processor and modifying the firmware are done through the SWD port that STM32 microcontrollers usually have to program their internal flash memory and debug developments. This SWD port is located on the PCB unpopulated, that is, without soldered pins or connectors.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;SWD&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/swd.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;To make the connection to this port there are several options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Solder wires directly.&lt;/li&gt;&lt;li&gt;Insert bare wires or male pin wires, tilting them to force contact.&lt;/li&gt;&lt;li&gt;Use specialized &lt;a href=&#34;https://www.amazon.es/gp/product/B009UOHE1K/&#34;&gt;connection probe clips&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Use &lt;a href=&#34;https://es.aliexpress.com/item/1005007602295920.html&#34;&gt;pogo pin clips&lt;/a&gt; (Single row 5pin).&lt;/li&gt;&lt;li&gt;Route the SWDIO and SWCLK pins to the USB-C port.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;hard-version&#34;&gt;Hard version&lt;a class=&#34;headerlink&#34; href=&#34;#hard-version&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Since I started tinkering with the machine before the microSD mod appeared, at the time I routed the SWD port to the USB-C. If you end up doing the microSD mod, the effort is not very worthwhile, since from then on flashing the firmware will be done from the microSD itself. However, for documentation purposes we will describe how the routing would be done.&lt;/p&gt;&lt;p&gt;The USB-C port only has the power pins connected since it is only intended to charge the machine. Therefore, the D+ and D- pins that are free can be used to wire them to the SWDIO and SWCLK pins, which are the only essential ones for the SWD port. We will also need to connect to 5V and GND, but these are already wired in the USB-C port. The routing is done with fine enameled wire like the one used to build coils. Soldering to the two central pins of the USB-C port which correspond to D+ and D- is complicated having a very small pitch, but with patience and flux it can be achieved.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/ruteado_usb-c_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Once the only difficult part of the procedure is solved, that is, soldering the wires to the USB port, these are secured every so often with Kapton tape and directed towards the battery area where the recess on both sides of the housing that surrounds it is used to pass one of the cables. The other is routed through the space between the battery housing and the PCB. This way both cables cannot touch each other, although in theory being varnished there should be no problem, this separate path is adopted as an additional precaution.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/ruteado_usb-c_2.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Once the wires reach the vicinity of the SWD port, the two test pads that can be seen in the photo are used to solder them.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Ruteado USB-C&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/ruteado_usb-c_3.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The correspondence between D+ and D- on the USB-C port with SWDIO and SWCLK of the SWD port can be done in any way as long as we are clear on where each one is to later make the USB-SWD adapter we will need. In this assembly the following correspondence has been chosen:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;D+ ↔ SWDIO&lt;/li&gt;&lt;li&gt;D- ↔ SWCLK&lt;/li&gt;&lt;/ul&gt;&lt;div class=&#34;admonition warning&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Warning&lt;/p&gt;&lt;p&gt;The idea at first was to adopt the correspondence used in &lt;a href=&#34;https://facelesstech.wordpress.com/2022/01/08/game-and-watch-hacking-with-rpi/&#34;&gt;this other guide&lt;/a&gt;, but due to a mistake made at the beginning, it ended up being done exactly the other way around. As mentioned earlier, it&#39;s really not a problem as long as you know how the connections are made. This warning is only in case someone tries to follow both guides simultaneously, so that this difference is taken into account.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Once the port is routed, we will need to build the adapter to extract the SWDIO and SWCLK signals from the USB-C port. To do this, cut a conventional USB-A cable to have access to the four separate cables it contains and solder female pin connectors to them. The adapter is completed with a USB-A to USB-C adapter that is marked to orient it correctly when we are going to use it, since we have only routed the D+ and D- pins on one of the two sides (the USB-C port is reversible).&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Adaptador USB-C SWD&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/adaptador_usb-c_swd.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The cable colors are usually standardized as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Red: 5V&lt;/li&gt;&lt;li&gt;Black: GND&lt;/li&gt;&lt;li&gt;Green: D+&lt;/li&gt;&lt;li&gt;White: D-&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&#34;easy-version&#34;&gt;Easy version&lt;a class=&#34;headerlink&#34; href=&#34;#easy-version&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The recommended way to make the connection to the SWD port (if you don&#39;t want to solder, which is the most reliable option) is using specialized &lt;a href=&#34;https://www.amazon.es/gp/product/B009UOHE1K/&#34;&gt;connection probe clips&lt;/a&gt;. To apply these clips, it is recommended to previously disconnect the console&#39;s battery. The battery connector pops off easily by inserting a thin piece of plastic underneath it.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Pinzas conexión GnW&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/pinzas_conexion_gnw.jpg&#34; /&gt;&lt;/p&gt;&lt;h2 id=&#34;programming-probe&#34;&gt;Programming probe&lt;a class=&#34;headerlink&#34; href=&#34;#programming-probe&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;We have solved where to connect on the console. Now we need a device capable of communicating with that SWD port of the microcontroller. The options are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.raspberrypi.com/products/raspberry-pi-pico/&#34;&gt;Raspberry Pi Pico&lt;/a&gt; (Recommended)&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.st.com/en/development-tools/st-link-v2.html&#34;&gt;STLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.segger.com/products/debug-probes/j-link/#models&#34;&gt;JLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://daplink.io/&#34;&gt;DAPLink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://projects.raspberrypi.org/en/projects/physical-computing/1&#34;&gt;Raspberry Pi (GPIO)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I chose to use a Raspberry Pi Pico. For it to work as an SWD programming probe, the &lt;a href=&#34;https://github.com/raspberrypi/debugprobe&#34;&gt;debugprobe&lt;/a&gt; firmware must be loaded. In my case I used the &lt;a href=&#34;https://github.com/raspberrypi/debugprobe/releases/download/picoprobe-cmsis-v1.0.3/picoprobe.uf2&#34;&gt;picoprobe.uf2&lt;/a&gt; file from &lt;a href=&#34;https://github.com/raspberrypi/debugprobe/releases/tag/picoprobe-cmsis-v1.0.3&#34;&gt;this specific release&lt;/a&gt;. To flash the Pico, just connect it to the computer via USB while pressing the &lt;code&gt;BOOTSEL&lt;/code&gt; button to force the mode in which it mounts as a storage drive and drag the &lt;code&gt;.uf2&lt;/code&gt; file to that drive. After autoflashing, the Pico will automatically restart and begin to function as an SWD programming probe.&lt;/p&gt;&lt;p&gt;Once the Pico is programmed, the only thing left is to make the connections of the cables that we had connected to the SWD port of the Game&amp;amp;Watch. We can use the same type of clips that we applied to the console, although in my case I used similar ones but slightly more robust, since the spacing of the GPIO pins of the Pico is much larger.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Pinzas conexión Pico&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/pinzas_conexion_pico.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The wiring between Game&amp;amp;Watch and Raspberry Pi Pico is:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Pico&lt;/th&gt;&lt;th style=&#34;text-align: left;&#34;&gt;GnW&lt;/th&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Color&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;3&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;3&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Black&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;4&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;5&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;White&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;5&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;2&lt;/td&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Green&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;img alt=&#34;GnW - Pico&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/gnw_pico.png&#34; /&gt;&lt;/p&gt;&lt;h2 id=&#34;microcontroller-unlocking&#34;&gt;Microcontroller unlocking&lt;a class=&#34;headerlink&#34; href=&#34;#microcontroller-unlocking&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once the connectivity to the board is solved, the first step in the modification is unlocking the STM32H7 microcontroller so that we can replace the firmware in its internal flash memory as well as write to the external flash memory (with code that we will inject into the microcontroller itself).&lt;/p&gt;&lt;p&gt;This task is performed with the &lt;a href=&#34;https://github.com/BrianPugh/gnwmanager&#34;&gt;gnwmanager&lt;/a&gt; utility suite. This package is registered on &lt;a href=&#34;https://pypi.org/project/gnwmanager/&#34;&gt;PyPI&lt;/a&gt; so it can be installed very easily with &lt;code&gt;pip&lt;/code&gt; (preferably inside a Python virtual environment).&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pip&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openocd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Now, after reconnecting the console&#39;s battery if we had disconnected it, we will execute the following command:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(gnwmanager) $ gnwmanager unlock&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The process will pause on a couple of occasions giving instructions through the terminal that we must follow. A typical output would be:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(gnwmanager) [edu@thinkpad ~]$ gnwmanager unlockIf interrupted, resume unlocking with: gnwmanager unlock --backup-dir=backups-2025-10-27-19-28-14Detected ZELDA game and watch.Backing up itcm to &amp;quot;backups-2025-10-27-19-28-14/itcm_backup_zelda.bin&amp;quot;... complete!Backing up external flash to &amp;quot;backups-2025-10-27-19-28-14/flash_backup_zelda.bin&amp;quot;... complete!Flashing payload to external flash... complete!Payload successfully flashed. Perform the following steps:1. Fully remove power, then re-apply power.2. Press the power button to turn on the device; the screen should turn blue.Press the &amp;quot;enter&amp;quot; key when the screen is blue: Backing up internal flash to &amp;quot;backups-2025-10-27-19-28-14/internal_flash_backup_zelda.bin&amp;quot;... complete!Unlocking device... complete!Restoring firmware... complete!Unlocking complete!Pressing the power button should launch the original firmware.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The files &lt;code&gt;internal_flash_backup_zelda.bin&lt;/code&gt;, &lt;code&gt;flash_backup_zelda.bin&lt;/code&gt; and &lt;code&gt;itcm_backup_zelda.bin&lt;/code&gt; will appear in a &lt;code&gt;backups-DATE-TIME&lt;/code&gt; directory that should be saved.&lt;/p&gt;&lt;p&gt;If we turn on the console at this point, we won&#39;t notice any change, meaning it will run the original firmware, but now the chip will be unprotected and we can flash another firmware to it.&lt;/p&gt;&lt;h2 id=&#34;modification-1-retro-go&#34;&gt;Modification 1: retro-go&lt;a class=&#34;headerlink&#34; href=&#34;#modification-1-retro-go&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h4 id=&#34;flash-replacement&#34;&gt;Flash replacement&lt;a class=&#34;headerlink&#34; href=&#34;#flash-replacement&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;At this time we start modifying the console seriously. As mentioned at the beginning, the microcontroller&#39;s external storage chip is a flash memory of only 4MB (Zelda model). Not only is it small for loading many games, but it has few write cycles, so replacing games frequently, or caching them when making the modification to add the microSD, can wear it out quickly. The usual modification is to desolder the original 4MB flash chip and replace it with a 64MB one, specifically the &lt;a href=&#34;https://es.aliexpress.com/item/1005005138227815.html&#34;&gt;MX25U51245GZ4I00&lt;/a&gt; model.&lt;/p&gt;&lt;p&gt;To remove the original flash chip, it is highly recommended to extract the console&#39;s motherboard, disconnecting the two data ribbon cables from the screen, desoldering the speaker, and removing all screws. Once we have the board on the workbench, we protect the surroundings of the chip with kapton tape and extract it by applying flux and hot air with a heat gun.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 1&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_flash_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 2&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_flash_2.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;Next, we clean the footprint of the chip we just removed with desoldering braid. Also, although it must be done with great care, it helps to scratch (with the tip of tweezers, for example) the solder mask of the PCB to expand outwards the solderable surface of the chip&#39;s solder footprint.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 3&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_flash_3.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The new flash chip has a surface mount QFN-8 format that is not easy to solder. It is recommended to apply flux and pre-tin the pads of said chip on the bottom. I also recommend putting a strip of kapton tape on the bottom so that it does not rest directly on the PCB, which would hinder the flow of solder through the pads.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Nuevo chip flash&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/new_flash.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;You must pay close attention to the dot on the chip&#39;s package that marks pin number 1, which corresponds to the one with an indicator triangle on the PCB as can be seen in the second photo of the following.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 4&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_flash_4.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retirar chip flash original 5&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_flash_5.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;flashing-retro-go&#34;&gt;Flashing retro-go&lt;a class=&#34;headerlink&#34; href=&#34;#flashing-retro-go&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Once the chip is installed, it&#39;s time to flash the firmware. For this modification, I choose not to set up dual boot, that is, not to install the patched original firmware alongside retro-go, to have all the flash memory space dedicated to games. To do this we start by downloading its repository:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/gitgit&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--recursive&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/sylverb/game-and-watch-retro-go&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-b&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;filesystem_wip&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;game-and-watch-retro-go&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Once the code is downloaded, it&#39;s time to install the ROMs in the directories corresponding to the different supported machines that we will find in &lt;code&gt;~/git/game-and-watch-retro-go/roms/&lt;/code&gt;. For example, to install the NES ROMs, we copy the ROMs to &lt;code&gt;~/git/game-and-watch-retro-go/roms/nes/&lt;/code&gt;. If we want cover art for the games, we will place the images in jpg or png format in the same directories and with the same file names of the corresponding ROMs. For example, for the &lt;code&gt;zelda.nes&lt;/code&gt; ROM, we will place the cover at &lt;code&gt;~/git/game-and-watch-retro-go/roms/nes/zelda.jpg&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;We have to keep in mind that all ROMs and their covers must fit in the 64 MB flash chip we installed in the console. Most ROMs compress, so as a starting rule, we will use the criteria of choosing ROMs for a total size of roughly double (128MB).&lt;/p&gt;&lt;p&gt;Now it&#39;s time to build the firmware (if we are not going to use covers to take advantage of the space of their files to be able to put more ROMs, do not include the &lt;code&gt;COVERFLOW&lt;/code&gt; and &lt;code&gt;JPG_QUALITY&lt;/code&gt; environment variables in the build and flash commands):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker_buildmake&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openocd&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;GNW_TARGET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;zelda&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;ADAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;cmsis-dap&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clean&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-j&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;When building finishes, we will be informed of the resulting binary size that will be flashed to the chip. For example:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;External flash usage Capacity: 61050880 Bytes ( 58.223 MB) Usage: 54335345 Bytes ( 51.818 MB) Free: 6715535 Bytes ( 6.404 MB)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;With that information in sight we can iterate the process adding or removing ROMs until obtaining a binary of about 55MB, since something must be anticipated for the game savestates.&lt;/p&gt;&lt;p&gt;Once we are satisfied with the space usage, we will flash the firmware reconnecting the programming probe discussed in the &lt;a href=&#34;#programming-probe&#34;&gt;Programming probe&lt;/a&gt; section.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;A typical output would be:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker@53335bdd9199:/opt/workdir$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COVERFLOW&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;JPG_QUALITY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;EXTFLASH_SIZE_MB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;make&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_gnwmanagerEntering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;LCD-Game-Emulator&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;blueMSX-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;caprice32-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;fceumm-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;gwenesis&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;potator&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;prosystem-go&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;retro-go-stm32&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;smw&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;tamalib&amp;#39;&lt;/span&gt;Entering&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;zelda3&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;BASH&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Checking&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;updated&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;roms&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;█████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;208&lt;/span&gt;/208&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;04&lt;/span&gt;:20&amp;lt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:00,&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;.25s/it&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;It is important that we keep the &lt;code&gt;build/gw_retro_go.elf&lt;/code&gt; file because we will need it to extract the savestates in the future.&lt;/p&gt;&lt;h4 id=&#34;backup-retro-go-savestates&#34;&gt;Backup retro-go savestates&lt;a class=&#34;headerlink&#34; href=&#34;#backup-retro-go-savestates&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;If later we want to extract the game savestates, to be able to reinstall them after flashing a new firmware, first we will need to place the &lt;code&gt;gw_retro_go.elf&lt;/code&gt; file we obtained in the previous step in the &lt;code&gt;build&lt;/code&gt; directory. Then we can restart the docker environment and run the backup script:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~/git/game-and-watch-retro-gomake&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;GNW_TARGET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;zelda&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;ADAPTER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;cmsis-dap&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./scripts/saves_backup.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;build/gw_retro_go.elf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;The savestates will appear in the &lt;code&gt;~/git/game-and-watch-retro-go/save_states&lt;/code&gt; subdirectory. Once we have built and flashed a new firmware, we can restore them by running the restoration script:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;docker&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./scripts/saves_restore.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Restoration is done intelligently, that is, if we have savestates of a game that is not in the new firmware, that savestate will not be installed on the console, to avoid wasting flash memory space.&lt;/p&gt;&lt;h2 id=&#34;modification-2-retro-go-sd&#34;&gt;Modification 2: retro-go-sd&lt;a class=&#34;headerlink&#34; href=&#34;#modification-2-retro-go-sd&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;h4 id=&#34;microsd-slot-module-installation&#34;&gt;microSD slot module installation&lt;a class=&#34;headerlink&#34; href=&#34;#microsd-slot-module-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;As with the modification for normal retro-go, to use retro-go-sd you must start by making a hardware modification to the console. This time it is more complicated, especially the final phase of soldering the module directly onto the microcontroller&#39;s pins, which have a very small pitch.&lt;/p&gt;&lt;p&gt;The module consists of the microSD card reader itself and 4 components. All this soldered onto a flex-type PCB. The complete list of components is:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005008902154552.html&#34;&gt;microSD card reader&lt;/a&gt;&lt;/li&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005005677654015.html&#34;&gt;100kΩ 0603 resistor&lt;/a&gt; or 0805&lt;/li&gt;&lt;li&gt;2 &lt;a href=&#34;https://es.aliexpress.com/item/1005003512666695.html&#34;&gt;1μF 0603 capacitors&lt;/a&gt; or 0805&lt;/li&gt;&lt;li&gt;1 &lt;a href=&#34;https://es.aliexpress.com/item/1005007136710461.html&#34;&gt;RT9193-28GB voltage regulator&lt;/a&gt;. There are people who have reported problems with this model bought on Aliexpress and recommend the &#34;MIC5504-2.8YM5-TR&#34; as a replacement.&lt;/li&gt;&lt;li&gt;1 flex PCB: Here the version by &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/raw/refs/heads/main/assets/MicroSD_Zelda_Final.zip&#34;&gt;PrimoAngelo&lt;/a&gt; is used. The original one usually referenced in most guides is the one by &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/raw/refs/heads/main/assets/GnW_SD_v2.zip&#34;&gt;Tim Schuerewegen / hundshamer&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We will begin by soldering the components onto the flex PCB.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;microSD module&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_msd_1.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The next step as we said at the beginning will be the difficult step of installing the module in the console. We remove the four screws that fall on its surface and present it in a way that we get the pads of the flex to align with the pins of the microcontroller while these end up at their feet, that is, like this.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;microSD module&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_msd_2.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Once it is aligned to our liking, we will proceed to fix it in its position by driving in the four screws. Now is when we will have to arm ourselves with patience and high-quality flux to manage to solder the pads of the flex and simultaneously avoid bridges between them. It will be necessary to check the continuity of the solders as well as the non-existence of bridges between adjacent pins. If we finally succeed, all that remains is to add a solder point between the pad marked with &lt;code&gt;+4V&lt;/code&gt; and the capacitor terminal next to it.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;microSD module&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/mod_msd_3.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;bootloader-flashing&#34;&gt;Bootloader flashing&lt;a class=&#34;headerlink&#34; href=&#34;#bootloader-flashing&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The next step will be to flash the bootloader along with the console&#39;s patched original firmware to allow dual boot (this time it is worthwhile as we have practically unlimited space for ROMs on the microSD). From the directory where the console backup files are located and with a Python environment that has &lt;code&gt;gnwmanager&lt;/code&gt; installed, we will execute the following command:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;gnwmanager&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash-patch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;zelda&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;internal_flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--bootloader&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;A typical output would be:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;gnwmanager&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gnwmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash-patch&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;zelda&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;internal_flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;flash_backup_zelda.bin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--bootloader&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;%&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;███████████████████████████████████████████████████████████████████&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;/16&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:58&amp;lt;&lt;span class=&#34;m&#34;&gt;00&lt;/span&gt;:00,&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;.66s/it&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;If we turn on the console at that moment, after removing the programming probe, we will see that the (patched) original firmware starts. After pressing &lt;code&gt;GAME + LEFT&lt;/code&gt;, the bootloader will execute and, as Retro-Go SD is not yet installed in the internal flash of the STM32 at that moment, we will be shown the following warning:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Bootloader warning&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/bootloader_warning.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;retro-go-sd-installation&#34;&gt;Retro-Go SD installation&lt;a class=&#34;headerlink&#34; href=&#34;#retro-go-sd-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;All that remains is to install the Retro-Go SD firmware. To do this, we will copy the &lt;code&gt;retro-go_update.bin&lt;/code&gt; file that we will download from the latest release published by Sylverb in the &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/releases&#34;&gt;Retro-Go SD repository&lt;/a&gt; to the root directory of the microSD. When booting for the first time, we will see on screen how the retro-go files and emulator cores are copied to the microSD itself.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Retro-Go SD installation&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/retro-go_update.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;At the same time, the following directory structure will be created on the microSD:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;retro-go/├── CONFIG├── bios/│ ├── coleco/│ ├── msx/│ ├── nes/├── cores/│ ├── mappers/│ ├── a2600.bin│ ├── a2600_defprops.bin│ ├── a7800.bin│ └── ...├── data/│ ├── a2600/│ ├── a7800/│ ├── amstrad/│ └── ...├── fonts/│ ├── cp1251_greybeard.bin│ ├── cp1251_sans_serif.bin│ ├── cp1251_sans_serif_bold.bin│ └── ...└── roms/ ├── a2600/ ├── a7800/ ├── amstrad/ └── ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;All that remains is to copy the ROMs we want to emulate to the corresponding directories. If we want cover art for the games, we will need to create the &lt;code&gt;covers&lt;/code&gt; subdirectory with the same subdirectories as the &lt;code&gt;roms&lt;/code&gt; directory and place the images there. These must be in a special IMG format that we will generate with a &lt;a href=&#34;https://github.com/sylverb/game-and-watch-retro-go-sd/blob/main/README.md#cover-art-generator-gencoverspy&#34;&gt;script included in the Retro-Go SD repository&lt;/a&gt;. The IMG file names must match those of the ROMs in their corresponding directory.&lt;/p&gt;&lt;h4 id=&#34;back-case&#34;&gt;Back case&lt;a class=&#34;headerlink&#34; href=&#34;#back-case&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;When installing the microSD slot, you need to modify the back cover of the console because the reader falls over some support posts on the d-pad. You also need to drill a hole in the side to allow access to the microSD so you can extract and insert it when you want to change games, rescue savestates, or update the firmware. To perform the drilling, &lt;a href=&#34;https://www.printables.com/model/1269910-zelda-game-and-watch-sd-card-drill-jig/files&#34;&gt;there are some tools&lt;/a&gt; designed by the user facelesstech that help keep the slot clean. You can see how this modification is performed in the video at the end of this article.&lt;/p&gt;&lt;p&gt;There is an alternative if we don&#39;t want to risk permanently damaging the original cover, and that is to 3D print the model created by the user Aradia, which can be downloaded &lt;a href=&#34;../files/posts/2026-03-08_zelda_gw/G%26W%20Zelda%20-%20Coque%20arri%C3%A8re%20-%20Mod%20Retro%20Go%20SD%20-%20v1.stl&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Back case&#34; src=&#34;../images/posts/2026-03-08_zelda_gw/carcasa.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The green color I found most similar to the Zelda console is the &lt;a href=&#34;https://www.amazon.es/dp/B0DHKZCL75&#34;&gt;15-Green from OVERTURE&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&#34;controls&#34;&gt;Controls&lt;a class=&#34;headerlink&#34; href=&#34;#controls&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Below we indicate the key combinations for controlling Retro-go SD (although many of them also work in Retro-go). Holding down the &lt;code&gt;PAUSE/SET&lt;/code&gt; button while pressing other buttons performs the following actions:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key combination&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;GAME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Save a screenshot (disabled by default in builds with 1MB flash)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;TIME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Change execution speed (1x/1.5x)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;UP&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Increase brightness&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;DOWN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Decrease brightness&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;RIGHT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Increase volume&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;LEFT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Decrease volume&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Load state&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Save state&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Autofire on button A&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Autofire on button B&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;POWER&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Poweroff without savestate&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;POWER&lt;/code&gt;&lt;/td&gt;&lt;td&gt;After having done &lt;code&gt;PAUSE/SET&lt;/code&gt; and selected &lt;code&gt;Power off&lt;/code&gt;, the bootloader will start without calling Retro-go, allowing us to see the bootloader version as well as information about the microSD detection and the state of the internal and external flash memories&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PAUSE/SET&lt;/code&gt; + &lt;code&gt;GAME&lt;/code&gt; + &lt;code&gt;LEFT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;From the OFW, it causes the bootloader to start without fully loading retro-go. It is used to see the screen that shows the SD detection, flash chip, and code in bank2 of the internal flash; it is also used to see the bootloader version.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GAME&lt;/code&gt; + menu &lt;code&gt;Reset settings&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Clears the registration data in the SD of what is cached in the flash; use when changing the SD of the machine or when changing the flash chip. It is equivalent to deleting the file &lt;code&gt;flashcachedata.bin&lt;/code&gt; in the &lt;code&gt;data&lt;/code&gt; directory of the SD.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;a class=&#34;headerlink&#34; href=&#34;#references&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I would not like to finish without mentioning the &lt;a href=&#34;https://discord.gg/TKjHZ5yV&#34;&gt;stacksmashing&lt;/a&gt; Discord server and the following users whose work has made these modifications possible:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;stacksmashing&lt;/li&gt;&lt;li&gt;kbeckmann&lt;/li&gt;&lt;li&gt;Tim Schuerewegen&lt;/li&gt;&lt;li&gt;BrianPugh&lt;/li&gt;&lt;li&gt;GMMan&lt;/li&gt;&lt;li&gt;tfmoe__&lt;/li&gt;&lt;li&gt;Sylver Bruneau&lt;/li&gt;&lt;li&gt;facelesstech&lt;/li&gt;&lt;li&gt;DasBoss&lt;/li&gt;&lt;li&gt;bxhxx&lt;/li&gt;&lt;li&gt;Benjamin Solberg&lt;/li&gt;&lt;li&gt;orzeus&lt;/li&gt;&lt;li&gt;ducalex&lt;/li&gt;&lt;li&gt;ZimM&lt;/li&gt;&lt;li&gt;Ninoh-FOX&lt;/li&gt;&lt;li&gt;PrimoAngelo&lt;/li&gt;&lt;li&gt;Aradia&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the following Macho Nacho video you can see the whole console modification process (the flex of the microSD module is the model that is not held by screws but with solder points on the G&amp;amp;W PCB):&lt;/p&gt;&lt;iframe width=&#34;688&#34; height=&#34;387&#34; src=&#34;https://www.youtube.com/embed/8YIHjUM8Qms&#34; title=&#34;Making The Zelda G&amp;W Into A MODERN Emulation Powerhouse&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;</description><link>https://apuntes.eduardofilo.es/en/2026-03-08_zelda_gw.html</link> <pubDate>Sun, 08 Mar 2026 15:45:00 +0100</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/en/2026-03-08_zelda_gw.html</guid> </item> <item> <title>Instalación Arch Linux en Lenovo ThinkPad T480</title> <description>&lt;p&gt;&lt;img alt=&#34;Lenovo ThinkPad T480 con Arch Linux&#34; src=&#34;images/posts/2025-06-28_thinkpad_t480/thinkpad_t480.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Tras cruzarme por casualidad con varios vídeos contando las excelencias del Lenovo ThinkPad T480 y de estar ligeramente disgustado con el portátil que adquirí el verano pasado como equipo principal, decidí comprar uno de segunda mano. En concreto el modelo que compré es el T480 con un procesador Intel Core i5-8250U, 16GB de RAM y disco SSD de 1TB. Costó 252€ puesto en casa al día siguiente, y fue adquirido en un vendendor especializado en reacondicionar equipos del que también vi algún vídeo por lo que esperaba que el equipo estuviera en buen estado. Así fue. El equipo llegó con un aspecto impecable, como nuevo, con sus dos baterías funcionales (la interna y la externa). El ThinkPad T480, en su momento tenía muchas opciones de configuración a la hora de adquirirlo. Por este motivo, al comprarlo en una de estas empresas de reacondicionamiento, es posible que el equipo tenga características distintas. Esta es la hoja de especificaciones del equipo donde pueden verse todas las opciones de hardware: &lt;a href=&#34;https://psref.lenovo.com/syspool/Sys/PDF/ThinkPad/ThinkPad_T480/ThinkPad_T480_Spec.PDF&#34;&gt;PSREF Lenovo ThinkPad T480&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;El Lenovo ThinkPad T480 es un portátil de 14&#34; con un teclado excelente, una pantalla IPS FullHD (1920x1080) y una buena conectividad (2 puertos USB-C uno de ellos Thunderbolt, 2 puertos USB-A 3.0, HDMI, Ethernet, lector de tarjetas SD y jack de auriculares/micrófono). Internamente es muy modificable, ya que tiene la RAM en dos sockets SODIMM, el disco SSD en un socket SATA que se puede convertir en M.2, un par de puertos PCIe donde se puede conectar más almacenamiento intento SSD o cosas como un modem 5G y la batería interna es también sustituible fácilmente. Además, la &lt;a href=&#34;https://wiki.archlinux.org/title/Lenovo_ThinkPad_T480&#34;&gt;compatibilidad con Linux está asegurada&lt;/a&gt;, ya que Lenovo proporciona drivers para Linux y la comunidad ha desarrollado drivers para el resto de componentes.&lt;/p&gt;&lt;p&gt;Lo que sigue es el procedimiento completo que he utilizado para instalar Arch Linux en él.&lt;/p&gt;&lt;h2 id=&#34;reparticionado-del-disco&#34;&gt;Reparticionado del disco&lt;a class=&#34;headerlink&#34; href=&#34;#reparticionado-del-disco&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;El equipo trae Windows 11 Pro instalado ocupando todo el disco. El plan es mantenerlo y hacer sitio para instalar Arch Linux junto a él. El tamaño de 1TB del disco SSD del equipo es perfecto para poder alojar dos sistemas operativos sin problemas.&lt;/p&gt;&lt;p&gt;El primer paso es encoger la partición de Windows. Para poder hacerlo, BitLocker debe estar desactivado. En la instalación de Windows 11 que trae el equipo, BitLocker estaba ya desactivado. La partición la podemos encoger con el propio Windows en marcha, desde el &lt;code&gt;Administrador de discos&lt;/code&gt; (buscándolo en el menú Inicio o pulsando Win+X). Una vez en el Administrador de discos, pulsamos con el botón derecho del ratón sobre la partición &lt;code&gt;C:&lt;/code&gt; y seleccionamos la opción &lt;code&gt;Reducir volumen...&lt;/code&gt;. En mi caso, reduje el volumen en 750GB, quedando la partición de Windows con un tamaño de unos 220GB.&lt;/p&gt;&lt;p&gt;En las instalaciones de Windows, hay una patición pequeña al final del disco que es la partición de recuperación. Al encoger la partición principal, el espacio libre queda antes de esta partición y por tanto, al crear las particiones para Linux más adelante, éstas tendrán un número superior a la partición de recuperación, pero se encontrarán antes. Esto en sistemas de archivos GPT no es un problema.&lt;/p&gt;&lt;h2 id=&#34;instalacion-base&#34;&gt;Instalación base&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-base&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para el proceso de instalación del sistema base me decanté por el camino convencional siguiendo las instrucciones de la &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;guía de instalación&lt;/a&gt; en el wiki de Arch. Dicha instalación utiliza una ISO de un sistema Arch que podremos arrancar desde una unidad extraíble USB con el que instalaremos los paquetes mínimos para tener un sistema arrancable y muy básico.&lt;/p&gt;&lt;p&gt;Antes de comenzar, comentar que por las características tan dinámicas de la distribución Arch Linux, es muy posible que esta guía quede desactualizada en algún momento. Por lo que recomiendo consultar la &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;guía oficial&lt;/a&gt; en caso de encontrar que algo no sale como se supone.&lt;/p&gt;&lt;p&gt;A continuación vemos paso a paso el proceso de instalación:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Descargar la &lt;a href=&#34;https://archlinux.org/download/&#34;&gt;ISO de Arch Linux&lt;/a&gt;. En concreto en mi caso utilicé &lt;a href=&#34;https://ftp.rediris.es/mirror/archlinux/iso/2025.06.01/archlinux-2025.06.01-x86_64.iso&#34;&gt;ésta versión&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Instalar la ISO en un pendrive. Puede utilizarse un programa como &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; o el comando &lt;code&gt;dd&lt;/code&gt; de Linux si se &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;sabe manejar&lt;/a&gt; (donde &lt;code&gt;/dev/sdX&lt;/code&gt; es el dispositivo del pendrive; puede comprobarse con el comando &lt;code&gt;lsblk&lt;/code&gt;):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;dd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;bs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4M&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;archlinux-2025.06.01-x86_64.iso&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/sdX&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;conv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;fsync&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;oflag&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;direct&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;progress&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Arrancar el portátil con el pendrive insertado. Pulsar &lt;code&gt;Enter&lt;/code&gt; cuando ofrece interrumpir el proceso normal de arranque y acto seguido &lt;code&gt;F12&lt;/code&gt;. Esto hace que el ordenador arranque en el &lt;code&gt;Boot Menu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Seleccionar la opción para arrancar desde el dispositivo extraíble que aparecerá en el menú como &lt;code&gt;USB HDD: Mass Storage Device&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Una vez que termine de arrancar el sistema live de instalación de Arch, ejecutar los siguiente comandos en orden. En caso de conectar a la red por cable Ethernet, se puede evitar la sesión con &lt;code&gt;iwctl&lt;/code&gt; (en la cual sustituiremos &lt;code&gt;&amp;lt;SSID&amp;gt;&lt;/code&gt; por nuestro SSID e introduciremos la contraseña cuando se nos pida):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# loadkeys es&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# iwctl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 scan&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 get-networks&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 connect &amp;lt;SSID&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# timedatectl&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# fdisk /dev/sda&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;En el programa &lt;code&gt;fdisk&lt;/code&gt; crear las siguientes particiones (en mi caso):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;sda5&lt;/code&gt;: 8GB, tipo &lt;code&gt;Linux swap&lt;/code&gt;, con el identificador &lt;code&gt;19&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;sda6&lt;/code&gt;: Espacio restante (724,4GB), tipo &lt;code&gt;Linux filesystem&lt;/code&gt;, con el identificador &lt;code&gt;20&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Salir de &lt;code&gt;fdisk&lt;/code&gt; escribiendo &lt;code&gt;w&lt;/code&gt; y pulsando &lt;code&gt;Enter&lt;/code&gt;. Continuar con los siguientes comandos (sustituir &lt;code&gt;&amp;lt;USER&amp;gt;&lt;/code&gt; por el nombre de usuario que se quiera crear):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# mkfs.ext4 /dev/sda6# mkswap /dev/sda5# mount /dev/sda6 /mnt# mount --mkdir /dev/sda1 /mnt/boot/efi# swapon /dev/sda5# pacstrap -K /mnt base base-devel linux linux-firmware sudo vi ntfs-3g networkmanager intel-ucode grub efibootmgr os-prober# genfstab -U /mnt &amp;gt;&amp;gt; /mnt/etc/fstab# arch-chroot /mnt# ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime# hwclock --systohc# vi /etc/locale.gen # Descomentar &amp;#39;en_US.UTF-8 UTF-8&amp;#39; y &amp;#39;es_ES.UTF-8 UTF-8&amp;#39;# locale-gen# echo &amp;quot;LANG=es_ES.UTF-8&amp;quot; &amp;gt; /etc/locale.conf# echo &amp;quot;KEYMAP=es&amp;quot; &amp;gt; /etc/vconsole.conf# echo &amp;quot;thinkpad&amp;quot; &amp;gt; /etc/hostname# echo -e &amp;quot;\n127.0.0.1 localhost\n::1 localhost\n127.0.1.1 thinkpad.localdomain thinkpad&amp;quot; &amp;gt;&amp;gt; /etc/hosts# passwd# useradd -m -G wheel,audio,video,storage -s /bin/bash &amp;lt;USER&amp;gt;# passwd &amp;lt;USER&amp;gt;# visudo # Descomentar &amp;#39;%wheel ALL=(ALL) ALL&amp;#39;# systemctl enable NetworkManager# vi /etc/default/grub # Descomentar &amp;#39;GRUB_DISABLE_OS_PROBER=false&amp;#39;# os-prober# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB# grub-mkconfig -o /boot/grub/grub.cfg# su - &amp;lt;USER&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;instalacion-entorno-grafico&#34;&gt;Instalación entorno gráfico&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-entorno-grafico&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuamos donde nos habíamos quedado, es decir en la sesión chroot desde el sistema live de instalación de Arch Linux. Lo haremos ya con el usuario normal (no root) por lo que a partir de ahora haremos uso con frecuencia de &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Ahora vamos a instalar el entorno gráfico, en mi caso XFCE4. En caso de elegir otro, &lt;a href=&#34;https://wiki.archlinux.org/title/Desktop_environment#Officially_supported&#34;&gt;aquí&lt;/a&gt; hay una lista de entornos gráficos soportados oficialmente por Arch Linux y las instrucciones correspondientes.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-Syy$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xfce4&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xfce4-goodies&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xorg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xdg-user-dirs&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pavucontrol&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gvfs&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gvfs-smb&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;thunar-archive-plugin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;unzip&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;unrar&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm-gtk-greeter&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm-gtk-greeter-settings&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire-pulse&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire-alsa&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;wireplumber&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cmake$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Instalar el gestor de paquetes &lt;code&gt;yay&lt;/code&gt; para poder instalar paquetes desde AUR:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/yay.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Por último instalar algunos programas y paquetes que nos serán útiles:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox-i18n-es&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;libreoffice-fresh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gimp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc-plugins-all&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;usbutils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;htop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;btop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openssh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;man-pages-es&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xdg-user-dirs-gtk&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsa-utils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;network-manager-applet&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blueman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;fwupd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gparted&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;terminator&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gimp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;shutter&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;audacity&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openshot&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ghex&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;net-tools&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshuttle&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gucharmap$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--now&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cerramos la sesión chroot, desmontamos las particiones y reiniciamos el sistema:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# umount -R /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# reboot&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;applet-networkmanager-y-bluetooth&#34;&gt;Applet NetworkManager y Bluetooth&lt;a class=&#34;headerlink&#34; href=&#34;#applet-networkmanager-y-bluetooth&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para que el applet de NetworkManager funcione correctamente, debemos instalar el paquete &lt;code&gt;network-manager-applet&lt;/code&gt; y añadirlo al autostart de XFCE4. Vamos a hacer algo parecido con el Bluetooth. Para ello, una vez iniciado el entorno gráfico, ejecutar:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;network-manager-applet&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blueman$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;start&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bluetooth$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--now&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bluetooth&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Ahora debemos añadir los applets al autostart de XFCE4. Para ello, ir a &lt;code&gt;Configuración &amp;gt; Sesión y arranque &amp;gt; Aplicaciones de inicio&lt;/code&gt; y pulsar en &lt;code&gt;Añadir&lt;/code&gt;. En el campo &lt;code&gt;Nombre&lt;/code&gt; escribir &lt;code&gt;NetworkManager Applet&lt;/code&gt;, en &lt;code&gt;Comando&lt;/code&gt; escribir &lt;code&gt;nm-applet&lt;/code&gt; y en &lt;code&gt;Comentario&lt;/code&gt; podemos dejarlo vacío o poner algo como &lt;code&gt;Applet de NetworkManager&lt;/code&gt;. Hacemos lo mismo para el Bluetooth, añadiendo un nuevo applet con el nombre &lt;code&gt;Bluetooth&lt;/code&gt;, el comando &lt;code&gt;blueman-applet&lt;/code&gt; y un comentario opcional.&lt;/p&gt;&lt;h2 id=&#34;instalacion-pamac&#34;&gt;Instalación pamac&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-pamac&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para facilitar la instalación de paquetes desde la interfaz gráfica, vamos a instalar &lt;code&gt;pamac&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pamac-aur&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Una vez instalado, abrirlo (buscando &lt;code&gt;Añadir/Quitar software&lt;/code&gt; en el cajón de aplicaciones) y activar el soporte AUR en &lt;code&gt;Preferencias &amp;gt; Terceros &amp;gt; Activar soporte de AUR&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusión&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Y esto sería todo. Ahora tenemos un sistema dualboot Arch Linux / Windows en el ThinkPad T480.&lt;/p&gt;&lt;h2 id=&#34;enlaces-de-interes&#34;&gt;Enlaces de interés&lt;a class=&#34;headerlink&#34; href=&#34;#enlaces-de-interes&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;Guía de instalación de Arch Linux&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Lenovo_ThinkPad_T480&#34;&gt;Instalación y configuración de Arch Linux en un portátil Lenovo ThinPad T480&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://psref.lenovo.com/syspool/Sys/PDF/ThinkPad/ThinkPad_T480/ThinkPad_T480_Spec.PDF&#34;&gt;Hoja de especificaciones del Lenovo ThinPad T480&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Fwupd&#34;&gt;Actualización de firmwares con fwupd&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/boltgolt/howdy&#34;&gt;Howdy for Linux&lt;/a&gt;: Howdy proporciona autenticación estilo Windows Hello™ para Linux. Usa los emisores IR y la cámara integrados junto con reconocimiento facial para verificar tu identidad.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://libreboot.org/docs/install/t480.html&#34;&gt;Install Libreboot on Lenovo ThinkPad T480 and/or T480s&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Problema en el firmware Thunderbolt:&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://pcsupport.lenovo.com/gb/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480s-type-20l7-20l8/solutions/ht508988-critical-intel-thunderbolt-software-and-firmware-updates-thinkpad&#34;&gt;Critical Intel Thunderbolt Software and Firmware Updates - ThinkPad&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=TzpOLO0vVoM&#34;&gt;Sustitución EPROM&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>https://apuntes.eduardofilo.es/2025-06-28_thinkpad_t480.html</link> <pubDate>Sat, 28 Jun 2025 22:30:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/2025-06-28_thinkpad_t480.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2025-06-28_thinkpad_t480/thinkpad_t480.png" type="image/png" length="65578" /> </item> <item> <title>Arch Linux Installation on Lenovo ThinkPad T480</title> <description>&lt;p&gt;&lt;img alt=&#34;Lenovo ThinkPad T480 with Arch Linux&#34; src=&#34;../images/posts/2025-06-28_thinkpad_t480/thinkpad_t480.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;After stumbling upon several videos praising the Lenovo ThinkPad T480 and feeling slightly dissatisfied with the laptop I bought last summer as my main device, I decided to buy a second-hand one. Specifically, I bought the T480 model with an Intel Core i5-8250U processor, 16GB RAM, and a 1TB SSD. It cost €252 delivered the next day, and I purchased it from a specialized refurbisher whose videos I had also seen, so I expected the machine to be in good condition. And it was. The laptop arrived in impeccable condition, like new, with both its batteries functional (internal and external). The ThinkPad T480 originally had many configuration options available. For this reason, when buying it from a refurbisher, it may come with different hardware features. Here is the full spec sheet where all hardware options can be seen: &lt;a href=&#34;https://psref.lenovo.com/syspool/Sys/PDF/ThinkPad/ThinkPad_T480/ThinkPad_T480_Spec.PDF&#34;&gt;PSREF Lenovo ThinkPad T480&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The Lenovo ThinkPad T480 is a 14&#34; laptop with an excellent keyboard, an IPS FullHD screen (1920x1080), and good connectivity (2 USB-C ports, one of them Thunderbolt, 2 USB-A 3.0 ports, HDMI, Ethernet, SD card reader, and audio/mic jack). Internally it&#39;s very customizable, as it has two SODIMM RAM slots, an SSD in a SATA slot that can be converted to M.2, a couple of PCIe ports for additional SSD storage or a 5G modem, and the internal battery is also easily replaceable. Moreover, &lt;a href=&#34;https://wiki.archlinux.org/title/Lenovo_ThinkPad_T480&#34;&gt;Linux compatibility is excellent&lt;/a&gt;, as Lenovo provides Linux drivers and the community has developed drivers for the rest of the components.&lt;/p&gt;&lt;p&gt;What follows is the full procedure I used to install Arch Linux on it.&lt;/p&gt;&lt;h2 id=&#34;disk-repartitioning&#34;&gt;Disk repartitioning&lt;a class=&#34;headerlink&#34; href=&#34;#disk-repartitioning&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The laptop comes with Windows 11 Pro installed occupying the entire disk. The plan is to keep it and make room to install Arch Linux alongside. The 1TB SSD is perfect to accommodate two operating systems without issues.&lt;/p&gt;&lt;p&gt;The first step is to shrink the Windows partition. To do so, BitLocker must be disabled. In the Windows 11 installation that came with the machine, BitLocker was already disabled. We can shrink the partition using Windows itself, through the &lt;code&gt;Disk Management&lt;/code&gt; tool (search in Start menu or press Win+X). Once there, right-click the &lt;code&gt;C:&lt;/code&gt; partition and select &lt;code&gt;Shrink Volume...&lt;/code&gt;. In my case, I shrank it by 750GB, leaving around 220GB for Windows.&lt;/p&gt;&lt;p&gt;Windows installations have a small recovery partition at the end of the disk. When shrinking the main partition, the free space appears before this recovery partition. Thus, when creating Linux partitions later, their numbering will be higher than the recovery partition but will physically be located before it. This is not a problem when using GPT partitioning schemes.&lt;/p&gt;&lt;h2 id=&#34;base-installation&#34;&gt;Base installation&lt;a class=&#34;headerlink&#34; href=&#34;#base-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For the base system installation, I chose the conventional route following the &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;installation guide&lt;/a&gt; from the Arch wiki. This installation uses an ISO of an Arch live system that we boot from a removable USB drive and use to install the minimal packages for a bootable, very basic system.&lt;/p&gt;&lt;p&gt;Before we begin, it&#39;s worth noting that due to the dynamic nature of Arch Linux, this guide may become outdated. I recommend checking the &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;official guide&lt;/a&gt; if something doesn&#39;t go as expected.&lt;/p&gt;&lt;p&gt;Here is the step-by-step installation process:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Download the &lt;a href=&#34;https://archlinux.org/download/&#34;&gt;Arch Linux ISO&lt;/a&gt;. I used &lt;a href=&#34;https://ftp.rediris.es/mirror/archlinux/iso/2025.06.01/archlinux-2025.06.01-x86_64.iso&#34;&gt;this version&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Flash the ISO to a USB stick using &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; or the &lt;code&gt;dd&lt;/code&gt; command in Linux (if you &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;know how to use it&lt;/a&gt;):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;dd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;bs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4M&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;archlinux-2025.06.01-x86_64.iso&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/sdX&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;conv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;fsync&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;oflag&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;direct&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;progress&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Boot the laptop with the USB stick inserted. Press &lt;code&gt;Enter&lt;/code&gt; when prompted to interrupt normal boot, then press &lt;code&gt;F12&lt;/code&gt; to enter the &lt;code&gt;Boot Menu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Select the option to boot from the removable device, listed as &lt;code&gt;USB HDD: Mass Storage Device&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once the Arch live system boots, run the following commands in order. If using a wired Ethernet connection, you can skip the &lt;code&gt;iwctl&lt;/code&gt; session (replace &lt;code&gt;&amp;lt;SSID&amp;gt;&lt;/code&gt; with your network name and enter the password when prompted):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# loadkeys es&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# iwctl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 scan&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 get-networks&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 connect &amp;lt;SSID&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# timedatectl&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# fdisk /dev/sda&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In &lt;code&gt;fdisk&lt;/code&gt;, create the following partitions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;sda5&lt;/code&gt;: 8GB, type &lt;code&gt;Linux swap&lt;/code&gt;, ID &lt;code&gt;19&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;sda6&lt;/code&gt;: Remaining space (724.4GB), type &lt;code&gt;Linux filesystem&lt;/code&gt;, ID &lt;code&gt;20&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Exit &lt;code&gt;fdisk&lt;/code&gt; by typing &lt;code&gt;w&lt;/code&gt; and pressing &lt;code&gt;Enter&lt;/code&gt;. Continue with the following commands (replace &lt;code&gt;&amp;lt;USER&amp;gt;&lt;/code&gt; with your desired username):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# mkfs.ext4 /dev/sda6# mkswap /dev/sda5# mount /dev/sda6 /mnt# mount --mkdir /dev/sda1 /mnt/boot/efi# swapon /dev/sda5# pacstrap -K /mnt base base-devel linux linux-firmware sudo vi ntfs-3g networkmanager intel-ucode grub efibootmgr os-prober# genfstab -U /mnt &amp;gt;&amp;gt; /mnt/etc/fstab# arch-chroot /mnt# ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime# hwclock --systohc# vi /etc/locale.gen # Uncomment &amp;#39;en_US.UTF-8 UTF-8&amp;#39; and &amp;#39;es_ES.UTF-8 UTF-8&amp;#39;# locale-gen# echo &amp;quot;LANG=es_ES.UTF-8&amp;quot; &amp;gt; /etc/locale.conf# echo &amp;quot;KEYMAP=es&amp;quot; &amp;gt; /etc/vconsole.conf# echo &amp;quot;thinkpad&amp;quot; &amp;gt; /etc/hostname# echo -e &amp;quot;\n127.0.0.1 localhost\n::1 localhost\n127.0.1.1 thinkpad.localdomain thinkpad&amp;quot; &amp;gt;&amp;gt; /etc/hosts# passwd# useradd -m -G wheel,audio,video,storage -s /bin/bash &amp;lt;USER&amp;gt;# passwd &amp;lt;USER&amp;gt;# visudo # Uncomment &amp;#39;%wheel ALL=(ALL) ALL&amp;#39;# systemctl enable NetworkManager# vi /etc/default/grub # Uncomment &amp;#39;GRUB_DISABLE_OS_PROBER=false&amp;#39;# os-prober# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB# grub-mkconfig -o /boot/grub/grub.cfg# su - &amp;lt;USER&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;graphical-environment-installation&#34;&gt;Graphical environment installation&lt;a class=&#34;headerlink&#34; href=&#34;#graphical-environment-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;We continue from where we left off, that is, in the chroot session from the Arch Linux live installer. From now on we&#39;ll use a regular user (not root), so we&#39;ll often use &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Now install the graphical environment — I chose XFCE4. If you prefer another one, &lt;a href=&#34;https://wiki.archlinux.org/title/Desktop_environment#Officially_supported&#34;&gt;here&lt;/a&gt; is a list of officially supported desktop environments and instructions.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-Syy$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xfce4&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xfce4-goodies&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xorg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xdg-user-dirs&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pavucontrol&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gvfs&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gvfs-smb&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;thunar-archive-plugin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;unzip&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;unrar&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm-gtk-greeter&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm-gtk-greeter-settings&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire-pulse&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pipewire-alsa&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;wireplumber&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;cmake$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lightdm&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Install the &lt;code&gt;yay&lt;/code&gt; package manager to install packages from AUR:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/yay.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Finally, install some useful applications and packages:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox-i18n-es&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;libreoffice-fresh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gimp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc-plugins-all&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;usbutils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;htop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;btop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openssh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;man-pages-es&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;xdg-user-dirs-gtk&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsa-utils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;network-manager-applet&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blueman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;fwupd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gparted&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;terminator&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;vlc&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gimp&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;shutter&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;audacity&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openshot&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ghex&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;net-tools&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshuttle&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;gucharmap$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--now&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Exit the chroot session, unmount the partitions, and reboot:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# umount -R /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# reboot&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;networkmanager-and-bluetooth-applets&#34;&gt;NetworkManager and Bluetooth Applets&lt;a class=&#34;headerlink&#34; href=&#34;#networkmanager-and-bluetooth-applets&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To make the NetworkManager applet work properly, we need to install the &lt;code&gt;network-manager-applet&lt;/code&gt; package and add it to XFCE4 autostart. We’ll do something similar for Bluetooth. Once in the graphical environment, run:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;network-manager-applet&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;blueman$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;start&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bluetooth$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;--now&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;bluetooth&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Now add both applets to XFCE4 autostart. Go to &lt;code&gt;Settings &amp;gt; Session and Startup &amp;gt; Application Autostart&lt;/code&gt; and click &lt;code&gt;Add&lt;/code&gt;. In &lt;code&gt;Name&lt;/code&gt;, enter &lt;code&gt;NetworkManager Applet&lt;/code&gt;, in &lt;code&gt;Command&lt;/code&gt;, enter &lt;code&gt;nm-applet&lt;/code&gt;, and you can leave &lt;code&gt;Comment&lt;/code&gt; empty or write something like &lt;code&gt;NetworkManager applet&lt;/code&gt;. Do the same for Bluetooth: name &lt;code&gt;Bluetooth&lt;/code&gt;, command &lt;code&gt;blueman-applet&lt;/code&gt;, optional comment.&lt;/p&gt;&lt;h2 id=&#34;installing-pamac&#34;&gt;Installing pamac&lt;a class=&#34;headerlink&#34; href=&#34;#installing-pamac&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To make graphical package management easier, install &lt;code&gt;pamac&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pamac-aur&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Once installed, open it (search for &lt;code&gt;Add/Remove Software&lt;/code&gt; in the application menu) and enable AUR support under &lt;code&gt;Preferences &amp;gt; Third Party &amp;gt; Enable AUR support&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;And that’s it. Now we have a dual boot Arch Linux / Windows system on the ThinkPad T480.&lt;/p&gt;&lt;h2 id=&#34;links-of-interest&#34;&gt;Links of interest&lt;a class=&#34;headerlink&#34; href=&#34;#links-of-interest&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;Arch Linux Installation Guide&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Lenovo_ThinkPad_T480&#34;&gt;Installation and configuration of Arch Linux on a Lenovo T480 laptop&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://psref.lenovo.com/syspool/Sys/PDF/ThinkPad/ThinkPad_T480/ThinkPad_T480_Spec.PDF&#34;&gt;Lenovo ThinkPad T480 Specification Sheet&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Fwupd&#34;&gt;Firmware updates with fwupd&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/boltgolt/howdy&#34;&gt;Howdy for Linux&lt;/a&gt;: Howdy provides Windows Hello™ style authentication for Linux. Use your built-in IR emitters and camera in combination with facial recognition to prove who you are.&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://libreboot.org/docs/install/t480.html&#34;&gt;Install Libreboot on Lenovo ThinkPad T480 and/or T480s&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Thunderbolt firmware problem:&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://pcsupport.lenovo.com/gb/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480s-type-20l7-20l8/solutions/ht508988-critical-intel-thunderbolt-software-and-firmware-updates-thinkpad&#34;&gt;Critical Intel Thunderbolt Software and Firmware Updates - ThinkPad&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=TzpOLO0vVoM&#34;&gt;EPROM replacement&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>https://apuntes.eduardofilo.es/en/2025-06-28_thinkpad_t480.html</link> <pubDate>Sat, 28 Jun 2025 22:30:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/en/2025-06-28_thinkpad_t480.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2025-06-28_thinkpad_t480/thinkpad_t480.png" type="image/png" length="65578" /> </item> <item> <title>ZX Mechtrum</title> <description>&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;images/posts/2024-09-09_zx_mechtrum/mechtrum_logo.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Hace tiempo que me apetecía montar un kit de &lt;a href=&#34;https://www.bytedelight.com/?product=harlequin-128k-rev-2d-black-large-diy-kit&#34;&gt;ZX Spectrum Harlequin 128&lt;/a&gt;. Es el Spectrum perfecto (128KB, soporte de todos los modelos/ROMs de las distintas versiones producidas por Sinclair y Amstrad, y con componentes que se pueden adquirir a día de hoy, incluida la ULA que está reemplazada por lógica discreta). Pero para la caja, la opción de adquirir una de las carcasas réplica del 48K original (gomas), no me entusiasmaba.&lt;/p&gt;&lt;p&gt;Hace unos meses conocí el proyecto &lt;a href=&#34;https://leesmithsworkshop.co.uk/products/the-mechtrum-mechanical-keyboard-zx-spectrum-case&#34;&gt;ZX Mechtrum&lt;/a&gt; de Lee Smith, una caja para placas de formato 48KB con un teclado mecánico, y me pareció el complemento perfecto para el Harlequin. De hecho, en el &lt;a href=&#34;https://www.youtube.com/watch?v=Gr8KhQHaJr4&#34;&gt;vídeo del canal More Fun Making It&lt;/a&gt; gracias al que supe de su existencia, su creador comenta que tiene el proyecto inmediato de instalar un Harlequin 128 en una de estas cajas.&lt;/p&gt;&lt;p&gt;Como ya existen varios vídeos y guías tanto del &lt;a href=&#34;https://www.youtube.com/watch?v=s1klr5vRye0&#34;&gt;kit Harlequin&lt;/a&gt; como de la caja &lt;a href=&#34;https://www.youtube.com/watch?v=O_FdXpOi2W4&#34;&gt;ZX Mechtrum&lt;/a&gt;, no voy a detallar el proceso de montaje, pero sí describirlo en general y comentar algunas particularidades y problemas que he encontrado.&lt;/p&gt;&lt;h4 id=&#34;mechtrum&#34;&gt;Mechtrum&lt;a class=&#34;headerlink&#34; href=&#34;#mechtrum&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Como el kit Harlequin 128 es distribuido por Byte Delight, por lo que su disponibilidad es bastante estable, el punto más crítico del plan era adquirir el &lt;a href=&#34;https://leesmithsworkshop.co.uk/products/the-mechtrum-mechanical-keyboard-zx-spectrum-case&#34;&gt;Mechtrum de Lee Smith&lt;/a&gt;, ya que la venta de productos no es su principal actividad. De hecho pude conseguir la última unidad de una tirada que hizo a finales de 2023. En el momento de escribir esto (septiembre 2024) vuelve a estar a la venta, pero no es de esperar que se mantenga siempre así. El caso es que pude adquirir una unidad que llegó ya montada. Mi unidad venía con 3 faceplates, una para el 48K, otra para el &lt;a href=&#34;https://github.com/UzixLS/zx-sizif-512/&#34;&gt;Sifzif 512&lt;/a&gt; y otra para el Harlequin 128.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;images/posts/2024-09-09_zx_mechtrum/mechtrum.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;harlequin-128&#34;&gt;Harlequin 128&lt;a class=&#34;headerlink&#34; href=&#34;#harlequin-128&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;El Harlequin 128, es un diseño de Don Superfo y puede adquirirse la PCB a través de los enlaces que hay en el &lt;a href=&#34;https://github.com/DonSuperfo/Superfo-Harlequin-128&#34;&gt;repositorio base del proyecto&lt;/a&gt;. En el repositorio se encuentra también la BOM (Bill Of Materials) para las distintas versiones con todos los componentes necesarios. Pero con diferencia la opción más sencilla es adquirir uno de los kits que distribuye Ben Versteeg, de &lt;a href=&#34;https://www.bytedelight.com/&#34;&gt;Byte Delight&lt;/a&gt;. El kit viene con una PCB de la versión issue 2D y todos los componentes necesarios para montarlo, convenientemente clasificados en bolsitas junto a un manual con el que es casi imposible perderse. También hay épocas en las que no está disponible en su tienda, pero es cuestión de estar atento o suscribirse a las notificaciones de disponibilidad. En mi caso tuve que esperar un par de meses a que volviera a estar disponible.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Harlequin 128&#34; src=&#34;images/posts/2024-09-09_zx_mechtrum/harlequin_128.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;El único problema que tuve durante su montaje fue que según el manual el array de resistencias R61 es opcional si no se va a instalar la interfaz de joystick. Pero esas resistencias, además de dar servicio al puerto de joystick, hacen de pullup para los microinterruptores de selección del banco de ROM, por lo que sin instalarlas, la selección de bancos de ROM superiores al 0 no era estable. Tras instalar el array R61, todo funcionó correctamente.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Harlequin 128 ROM selector pullups&#34; src=&#34;images/posts/2024-09-09_zx_mechtrum/harlequin_128_pullups.png&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;resultado&#34;&gt;Resultado&lt;a class=&#34;headerlink&#34; href=&#34;#resultado&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;El resultado es un sueño hecho realidad, un Spectrum 128K con teclado mecánico y componentes fiables. Además de una belleza. 🤩&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;images/posts/2024-09-09_zx_mechtrum/resultado.jpg&#34; /&gt;&lt;/p&gt;</description><link>https://apuntes.eduardofilo.es/2024-09-09_zx_mechtrum.html</link> <pubDate>Mon, 09 Sep 2024 12:00:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/2024-09-09_zx_mechtrum.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2024-09-09_zx_mechtrum/mechtrum_logo.png" type="image/png" length="278257" /> </item> <item> <title>ZX Mechtrum</title> <description>&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;../images/posts/2024-09-09_zx_mechtrum/mechtrum_logo.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;For a while, I’ve wanted to assemble a &lt;a href=&#34;https://www.bytedelight.com/?product=harlequin-128k-rev-2d-black-large-diy-kit&#34;&gt;ZX Spectrum Harlequin 128&lt;/a&gt; kit. It’s the perfect Spectrum (128KB, supports all models/ROMs from the various versions produced by Sinclair and Amstrad, and with components that can still be acquired today, including the ULA, which has been replaced by discrete logic). However, the option of purchasing a replica case of the original 48K (rubber keys) didn’t really excite me.&lt;/p&gt;&lt;p&gt;A few months ago, I came across Lee Smith’s &lt;a href=&#34;https://leesmithsworkshop.co.uk/products/the-mechtrum-mechanical-keyboard-zx-spectrum-case&#34;&gt;ZX Mechtrum&lt;/a&gt; project, a case for 48KB-format boards with a mechanical keyboard, and it seemed like the perfect complement for the Harlequin. In fact, in the &lt;a href=&#34;https://www.youtube.com/watch?v=Gr8KhQHaJr4&#34;&gt;video from the More Fun Making It channel&lt;/a&gt;, through which I learned of its existence, its creator mentions that he plans to install a Harlequin 128 in one of these cases soon.&lt;/p&gt;&lt;p&gt;Since there are already several videos and guides for both the &lt;a href=&#34;https://www.youtube.com/watch?v=s1klr5vRye0&#34;&gt;Harlequin kit&lt;/a&gt; and the &lt;a href=&#34;https://www.youtube.com/watch?v=O_FdXpOi2W4&#34;&gt;ZX Mechtrum&lt;/a&gt; case, I won’t detail the assembly process but will instead describe it generally and mention some peculiarities and problems I encountered.&lt;/p&gt;&lt;h4 id=&#34;mechtrum&#34;&gt;Mechtrum&lt;a class=&#34;headerlink&#34; href=&#34;#mechtrum&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Since the Harlequin 128 kit is distributed by Byte Delight, its availability is quite stable, so the most critical point of the plan was acquiring the &lt;a href=&#34;https://leesmithsworkshop.co.uk/products/the-mechtrum-mechanical-keyboard-zx-spectrum-case&#34;&gt;Mechtrum from Lee Smith&lt;/a&gt;, as selling products is not his main activity. In fact, I managed to get the last unit from a batch he made in late 2023. As of writing this (September 2024), it’s back on sale, but it’s not expected to remain available for long. In any case, I was able to acquire a unit that arrived pre-assembled. My unit came with 3 faceplates, one for the 48K, another for the &lt;a href=&#34;https://github.com/UzixLS/zx-sizif-512/&#34;&gt;Sifzif 512&lt;/a&gt;, and one for the Harlequin 128.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;../images/posts/2024-09-09_zx_mechtrum/mechtrum.jpg&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;harlequin-128&#34;&gt;Harlequin 128&lt;a class=&#34;headerlink&#34; href=&#34;#harlequin-128&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The Harlequin 128 is a design by Don Superfo, and the PCB can be purchased through the links provided in the &lt;a href=&#34;https://github.com/DonSuperfo/Superfo-Harlequin-128&#34;&gt;project’s base repository&lt;/a&gt;. The repository also contains the BOM (Bill Of Materials) for the different versions with all the necessary components. However, by far the easiest option is to purchase one of the kits distributed by Ben Versteeg from &lt;a href=&#34;https://www.bytedelight.com/&#34;&gt;Byte Delight&lt;/a&gt;. The kit comes with an issue 2D version PCB and all the necessary components to assemble it, conveniently sorted into bags along with a manual that makes it almost impossible to get lost. There are times when it’s not available in his store, but it’s just a matter of staying alert or subscribing to availability notifications. In my case, I had to wait a couple of months for it to become available again.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Harlequin 128&#34; src=&#34;../images/posts/2024-09-09_zx_mechtrum/harlequin_128.jpg&#34; /&gt;&lt;/p&gt;&lt;p&gt;The only problem I encountered during assembly was that, according to the manual, the R61 resistor array is optional if you’re not going to install the joystick interface. However, these resistors, in addition to serving the joystick port, also act as pullups for the ROM bank selection microswitches, so without them, selecting ROM banks higher than 0 was unstable. After installing the R61 array, everything worked correctly.&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Harlequin 128 ROM selector pullups&#34; src=&#34;../images/posts/2024-09-09_zx_mechtrum/harlequin_128_pullups.png&#34; /&gt;&lt;/p&gt;&lt;h4 id=&#34;resultado&#34;&gt;Resultado&lt;a class=&#34;headerlink&#34; href=&#34;#resultado&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The result is a dream come true: a 128K Spectrum with a mechanical keyboard and reliable components. And it’s a beauty. 🤩&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Mechtrum&#34; src=&#34;../images/posts/2024-09-09_zx_mechtrum/resultado.jpg&#34; /&gt;&lt;/p&gt;</description><link>https://apuntes.eduardofilo.es/en/2024-09-09_zx_mechtrum.html</link> <pubDate>Mon, 09 Sep 2024 12:00:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/en/2024-09-09_zx_mechtrum.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2024-09-09_zx_mechtrum/mechtrum_logo.png" type="image/png" length="278257" /> </item> <item> <title>Instalación Arch Linux en Steam Deck</title> <description>&lt;p&gt;&lt;img alt=&#34;Steam Deck con Arch Linux&#34; src=&#34;images/posts/2024-09-04_steam_deck_arch/steam_deck_logo.png&#34; /&gt;&lt;/p&gt;&lt;div class=&#34;admonition info&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Pérdida de dual boot al actualizar SteamOS&lt;/p&gt;&lt;p&gt;En la actualización de SteamOS a la rama 3.6 (en concreto la primera versión estable de esa rama fue la 3.6.19) se perdió la configuración del dualboot por lo que la máquina sólo arrancaba en SteamOS. La solución fue volver a seguir los pasos de &lt;a href=&#34;#instalacion-gestor-dualboot&#34;&gt;Instalación gestor dualboot&lt;/a&gt; a partir del paso 4.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;En julio de 2024 durante unas semanas, el modelo de 512GB de la Steam Deck LCD pudo adquirirse por debajo de los 400€. Un precio muy atractivo para una &lt;a href=&#34;https://www.steamdeck.com/es/tech/deck&#34;&gt;máquina decente&lt;/a&gt; con procesador AMD de arquitectura x86 (AMD64), 16GB de RAM, disco SSD tipo NVMe de la capacidad mencionada antes, pantalla táctil y controles de juego integrados. Es decir un PC consolizado, pero un PC al fin y al cabo. Pero sobre todo lo que hace para mi especialmente atractiva la máquina es su soporte Linux completo, lo que la convierte en una plataforma ideal para cacharrear con distribuciones Linux o utilizarlo como PC portátil secundario. Todo esto naturalmente además de su uso convencional para jugar y &lt;a href=&#34;https://www.emudeck.com/&#34;&gt;emular&lt;/a&gt; videojuegos.&lt;/p&gt;&lt;p&gt;SteamOS, el sistema operativo que trae de serie, está basado en Arch Linux, y en su modo escritorio, puede utilizarse prácticamente como un Arch Linux normal. La principal restricción es que está construido en modo inmutable y a pesar de que este tipo de configuración de sistema tiene sus virtudes y se entiende perfectamente que Valve lo haya escogido para su máquina, todavía prefiero un sistema normal (mutable), por lo que me decidí por instalar un Arch normal y aprender a instalar de paso esta distribución. Según el &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck&#34;&gt;wiki de Arch&lt;/a&gt;, el hardware del modelo LCD de la Steam Deck está soportado al 100% en Arch.&lt;/p&gt;&lt;p&gt;Lo que sigue es el procedimiento completo que he utilizado.&lt;/p&gt;&lt;h2 id=&#34;tecladoraton&#34;&gt;Teclado/ratón&lt;a class=&#34;headerlink&#34; href=&#34;#tecladoraton&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Aunque gran parte de las operaciones pueden hacerse utilizando la pantalla táctil y los trackpads de la Steam Deck, es muy recomendable conectar un teclado y ratón a la consola para facilitar la instalación, sobre todo durante la sesión de terminal con la que instalaremos el sistema base. La Steam Deck sólo posee un puerto USB-C, por lo que lo más práctico es utilizar un dock. También puede ser suficiente (sobre todo si ejecutamos los sistemas live desde una microSD) con un dongle de teclado/ratón inalámbricos conectados con un adaptador USB&amp;lt;-&amp;gt;USB-C, aunque en ese caso conviene asegurarse de que la Steam Deck tiene la batería completamente cargada.&lt;/p&gt;&lt;p&gt;En mi caso utilicé un dock &lt;a href=&#34;https://www.dell.com/es-es/shop/adaptador-multipuerto-usb-c-de-dell-7-en-1-da310/apd/470-aeup/conexi%C3%B3n-wifi-y-redes&#34;&gt;DA310z&lt;/a&gt; de Dell.&lt;/p&gt;&lt;h2 id=&#34;reparticionado-del-disco&#34;&gt;Reparticionado del disco&lt;a class=&#34;headerlink&#34; href=&#34;#reparticionado-del-disco&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;El disco SSD de la Steam Deck que adquirí es de 512GB y de tipo NVMe. Es el tamaño mínimo si queremos instalar otro sistema junto a SteamOS.&lt;/p&gt;&lt;p&gt;Antes de empezar con la instalación debemos hacer hueco para la misma encogiendo la partición principal (la número 8). Para ello podemos utilizar prácticamente cualquier distribución live Linux que arranquemos desde una unidad extraíble (USB o microSD) ya que todas ellas llevan algún programa para gestionar particiones. En mi caso utilicé una ISO del programa &lt;code&gt;gparted&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;El procedimiento completo puede verse a continuación:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Descargar la &lt;a href=&#34;https://gparted.org/download.php&#34;&gt;ISO de gparted&lt;/a&gt;. En concreto en mi caso utilicé &lt;a href=&#34;https://downloads.sourceforge.net/gparted/gparted-live-1.6.0-3-amd64.iso&#34;&gt;ésta versión&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Instalar la ISO en un pendrive o mejor aún en una microSD (ya que la Steam Deck posee una ranura para éstas). Puede utilizarse un programa como &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; o el comando &lt;code&gt;dd&lt;/code&gt; de Linux si se &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;sabe manejar&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Arrancar la Steam Deck con el pendrive o la microSD insertada manteniendo pulsado el botón de bajar el volumen hasta que se escuchen los tonos de encendido. Esto hace que la consola arranque en el menú &lt;code&gt;Boot Manager&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Seleccionar la opción de arrancar desde el dispositivo extraíble que aparecerá en el menú como &lt;code&gt;EFI USB Device (USB)&lt;/code&gt; o &lt;code&gt;EFI SD/MMC Card (XX XXXX XXXX)&lt;/code&gt; según si utilizamos un pendrive o una microSD respectivamente.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Aparece una pantalla de configuración en la que hacemos las siguientes selecciones:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Policy for handling keymaps =&amp;gt; Don&#39;t touch keymap&lt;/li&gt;&lt;li&gt;Which language do you prefer? =&amp;gt; 25&lt;/li&gt;&lt;li&gt;Which mode do you prefer? =&amp;gt; 0&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Tras aceptar la última opción anterior, arrancará el programa &lt;code&gt;gparted&lt;/code&gt; en modo gráfico (con la pantalla girada, eso sí). Haremos cambios hasta dejar las particiones como se ve en la foto. Básicamente lo que haremos será encoger la partición #8 unos 100GB, crear la #9 de 4GB para swap y la #10 con el espacio restante para el sistema raíz de Arch:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Particiones de la Steam Deck&#34; src=&#34;images/posts/2024-09-04_steam_deck_arch/steam-deck-partitions.png&#34; /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Destacar a la vista de las particiones que utiliza SteamOS que hace uso de un &lt;a href=&#34;https://blog.davidbyrne.dev/2018/08/16/linux-ab-partitions&#34;&gt;sistema de particiones A/B&lt;/a&gt;, habitual en Android, por el cual la mayoría de las particiones del sistema (excepto la de usuario) están duplicadas. Dicho sistema está pensado para facilitar las actualizaciones, o más bien para volver atrás en caso de problemas durante las mismas.&lt;/p&gt;&lt;h2 id=&#34;instalacion-base&#34;&gt;Instalación base&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-base&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para el proceso de instalación del sistema base me decanté por el camino convencional siguiendo las instrucciones de la &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;guía de instalación&lt;/a&gt; en el wiki de Arch. Dicha instalación utiliza una ISO de un sistema Arch que podremos arrancar desde una unidad extraíble (USB o microSD) con el que instalaremos los paquetes básicos para tener un sistema arrancable y muy básico.&lt;/p&gt;&lt;p&gt;Antes de comenzar, comentar que por las características tan dinámicas de la distribución Arch Linux, es muy posible que esta guía quede desactualizada en poco tiempo. Por lo que recomiendo consultar la &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;guía oficial&lt;/a&gt; en caso de encontrar que algo no sale como se supone.&lt;/p&gt;&lt;p&gt;A continuación vemos paso a paso el proceso de instalación:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Descargar la &lt;a href=&#34;https://archlinux.org/download/&#34;&gt;ISO de Arch Linux&lt;/a&gt;. En concreto en mi caso utilicé &lt;a href=&#34;https://es.mirrors.cicku.me/archlinux/iso/2024.08.01/archlinux-2024.08.01-x86_64.iso&#34;&gt;ésta versión&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Instalar la ISO en un pendrive o mejor aún en una microSD para utilizar la ranura que posee la Steam Deck. Puede utilizarse un programa como &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; o el comando &lt;code&gt;dd&lt;/code&gt; de Linux si se &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;sabe manejar&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Arrancar la Steam Deck con el pendrive o la microSD insertada manteniendo pulsado el botón de bajar el volumen hasta que se escuchen los tonos de encendido. Esto hace que la consola arranque en el menú &lt;code&gt;Boot Manager&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Seleccionar la opción de arrancar desde el dispositivo extraíble que aparecerá en el menú como &lt;code&gt;EFI USB Device (USB)&lt;/code&gt; o &lt;code&gt;EFI SD/MMC Card (XX XXXX XXXX)&lt;/code&gt; según si utilizamos un pendrive o una microSD respectivamente.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Una vez que termine de arrancar el sistema live de instalación de Arch, ejecutar los siguiente comandos en orden (en la sesión con &lt;code&gt;iwctl&lt;/code&gt; sustituiremos &lt;code&gt;&amp;lt;SSID&amp;gt;&lt;/code&gt; por el nuestro e introduciremos la contraseña cuando se nos pida; también sustituiremos el identificador de usuario &lt;code&gt;&amp;lt;USER&amp;gt;&lt;/code&gt; por el que queramos utilizar):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;34&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# loadkeys es&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# iwctl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 scan&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 get-networks&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 connect &amp;lt;SSID&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mkfs.ext4 /dev/nvme0n1p10&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mkswap /dev/nvme0n1p9&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mount /dev/nvme0n1p10 /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mount --mkdir /dev/nvme0n1p1 /mnt/boot/efi&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# swapon /dev/nvme0n1p9&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacstrap -K /mnt base base-devel linux linux-firmware sudo vi ntfs-3g networkmanager amd-ucode grub efibootmgr git cmake qt5-wayland&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# genfstab -U /mnt &amp;gt;&amp;gt; /mnt/etc/fstab&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# arch-chroot /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# hwclock --systohc&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# vi /etc/locale.gen # descomentar &amp;#39;en_US.UTF-8 UTF-8&amp;#39; y &amp;#39;es_ES.UTF-8 UTF-8&amp;#39;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# locale-gen&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;LANG=es_ES.UTF-8&amp;quot; &amp;gt; /etc/locale.conf&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;KEYMAP=es&amp;quot; &amp;gt; /etc/vconsole.conf&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;deck&amp;quot; &amp;gt; /etc/hostname&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo -e &amp;quot;\n127.0.0.1 localhost\n::1 localhost\n127.0.1.1 deck&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# passwd&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Arch&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# grub-mkconfig -o /boot/grub/grub.cfg&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# systemctl enable NetworkManager&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# useradd -m -G wheel,audio,video,storage &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# passwd &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# chmod u+w /etc/sudoers&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# vi /etc/sudoers # Descomentar &amp;#39;%wheel ALL=(ALL) ALL&amp;#39;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# chmod u-w /etc/sudoers&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacman -Syy&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacman -S xorg-server xf86-video-amdgpu maliit-keyboard qt5-wayland&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# su - &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;El password que elegiremos cuando ejecutemos el comando &lt;code&gt;passwd &amp;lt;USER&amp;gt;&lt;/code&gt; debe ser numérico si queremos utilizar Plasma Mobile, ya que la pantalla de desbloqueo sólo nos permite introducir un PIN.&lt;/p&gt;&lt;h2 id=&#34;instalacion-entorno-grafico&#34;&gt;Instalación entorno gráfico&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-entorno-grafico&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuamos donde nos habíamos quedado, es decir en la sesión chroot desde el sistema live de instalación de Arch Linux. Lo haremos ya con el usuario normal (no root) por lo que a partir de ahora haremos uso con frecuencia de &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Ahora vamos a instalar los entornos gráficos. Lo digo en plural porque vamos a instalar dos, Plasma y Plasma Mobile. Hago esto porque dependiendo de si vamos a utilizar la Steam Deck con Arch en modo escritorio (con teclado/ratón y posiblemente pantalla) o portátil (sin teclado/ratón), nos convendrá más un sistema u otro, dado que Plasma Mobile presenta un interfaz diseñado para ser manejado con una pantalla táctil (estilo tablet).&lt;/p&gt;&lt;p&gt;En realidad no es necesario instalar los dos escritorios, por eso se han separado sus pasos de instalación. Podemos instalar uno, el otro o ambos.&lt;/p&gt;&lt;h4 id=&#34;plasma&#34;&gt;Plasma&lt;a class=&#34;headerlink&#34; href=&#34;#plasma&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-meta&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Durante la instalación del paquete anterior se nos harán una serie de preguntas. A la tercera pregunta responder &lt;code&gt;2) noto-fonts&lt;/code&gt;. En el resto ofrecer la respuesta predeterminada (pulsando retorno).&lt;/p&gt;&lt;h4 id=&#34;plasma-mobile&#34;&gt;Plasma Mobile&lt;a class=&#34;headerlink&#34; href=&#34;#plasma-mobile&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Plasma Mobile no se encuentra todavía en los repositorios oficiales. Tenemos que utilizar &lt;a href=&#34;https://aur.archlinux.org/&#34;&gt;AUR&lt;/a&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Empezamos instalando algunas dependencias:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-workspace&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kcontacts&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kirigami2&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kpeople&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;libphonenumber&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Instalamos los paquetes de AUR &lt;code&gt;plasma-nano&lt;/code&gt;, &lt;code&gt;plasma-settings&lt;/code&gt; y &lt;code&gt;plasma-mobile&lt;/code&gt; en este orden:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-nano.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-nano$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-settings.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-settings$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-mobile.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-mobile$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h4 id=&#34;fin-de-instalacion-entorno-grafico&#34;&gt;Fin de instalación entorno gráfico&lt;a class=&#34;headerlink&#34; href=&#34;#fin-de-instalacion-entorno-grafico&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Instalamos el gestor de sesiones SDDM y algunas aplicaciones que nos harán falta para terminar la instalación desde el modo gráfico:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sddm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;konsole&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;dolphin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kwrite&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;partitionmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;power-profiles-daemon$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sddm$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cerramos la sesión chroot, desmontamos las particiones y reiniciamos el sistema:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# umount -R /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# reboot&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;configuracion-basica&#34;&gt;Configuración básica&lt;a class=&#34;headerlink&#34; href=&#34;#configuracion-basica&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Tras reiniciar deberíamos ver el gestor de sesiones SDDM, inicialmente con la pantalla girada y sin posibilidad de utilizar el teclado en pantalla, por lo que todavía continuaremos utilizando el teclado y ratón externos.&lt;/p&gt;&lt;p&gt;Una vez que hayamos iniciado sesión en Plasma o Plasma Mobile, lo primero que tenemos que hacer es configurar la conexión a internet. Para ello hacemos click sobre el icono correspondiente en la bandeja del sistema abajo a la derecha en Plasma o arriba a la derecha en Plasma Mobile.&lt;/p&gt;&lt;p&gt;Una vez que tengamos conexión, abrimos la aplicación Konsole y ejecutamos los siguientes comandos para instalar el paquete de aplicaciones básico de KDE, el navegador Firefox y el servidor SSH:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kde-applications-meta&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openssh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;usbutils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lshw&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;man-db&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;htop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;spectacle$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;start&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Finalmente hacemos las siguientes configuraciones:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Preferencias &amp;gt; Preferencias del sistema &amp;gt; Entrada y salida &amp;gt; Pantalla y monitor &amp;gt; Escalar&lt;/code&gt;: Configurar la escala de la pantalla a 110%.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferencias &amp;gt; Preferencias del sistema &amp;gt; Entrada y salida &amp;gt; Teclado &amp;gt; Distribuciones &amp;gt; Configurar distribuciones &amp;gt; Añadir&lt;/code&gt;: Añadir la distribución de teclado español.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferencias &amp;gt; Preferencias del sistema &amp;gt; Entrada y salida &amp;gt; Teclado &amp;gt; Teclado virtual&lt;/code&gt;: Seleccionar &lt;code&gt;Maliit&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferencias &amp;gt; Preferencias del sistema &amp;gt; Idioma y hora &amp;gt; Fecha y hora &amp;gt; Zona horaria&lt;/code&gt;: Seleccionar Madrid.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferencias &amp;gt; Preferencias del sistema &amp;gt; Aspecto y estilo &amp;gt; Texto y tipos de letra &amp;gt; Tipos de letra&lt;/code&gt;. Ajustar estos valores:&lt;ul&gt;&lt;li&gt;&lt;code&gt;General&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Anchura fija&lt;/code&gt;: &lt;code&gt;Hack 10pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Pequeña&lt;/code&gt;: &lt;code&gt;Noto Sans 9pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Barra de herramientas&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Menú&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Título de la ventana&lt;/code&gt;: &lt;code&gt;Noto Sans 10pt&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Botón derecho (L2) sobre la barra de tareas y seleccionar &lt;code&gt;Mostrar configuración del panel&lt;/code&gt;. En &lt;code&gt;Preferencias del panel &amp;gt; Altura del panel&lt;/code&gt; subir la altura a 54px.&lt;/li&gt;&lt;li&gt;Botón derecho (L2) sobre el icono que sirve para mostrar los iconos ocultos en la bandeja del sistema y seleccionar &lt;code&gt;Configurar Bandeja del sistema&lt;/code&gt;. En &lt;code&gt;General&lt;/code&gt; seleccionar estos valores:&lt;ul&gt;&lt;li&gt;&lt;code&gt;Tamaño de los iconos del panel&lt;/code&gt;: &lt;code&gt;Escalar a la altura del panel&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Espacio entre los iconos del panel&lt;/code&gt;: &lt;code&gt;Normal&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Botón derecho (L2) sobre el icono que sirve para mostrar los iconos ocultos en la bandeja del sistema y seleccionar &lt;code&gt;Mostrar configuración del panel&lt;/code&gt;. Veremos un minireloj que antes no se veía sobre el que pulsaremos con el botón derecho (L2). Seleccionamos &lt;code&gt;Mostrar alternativas&lt;/code&gt; y de los elementos gráficos alternativos que aparecen escogemos &lt;code&gt;Reloj digital&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;configuracion-de-sddm&#34;&gt;Configuración de SDDM&lt;a class=&#34;headerlink&#34; href=&#34;#configuracion-de-sddm&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Finalmente vamos a mejorar la configuración del gestor de sesiones SDDM para que permita el teclado en pantalla, tenga mejor aspecto y muestre la pantalla en la orientación correcta:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Crear fichero &lt;code&gt;/etc/sddm.conf.d/10-wayland.conf&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;[General]&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;DisplayServer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;wayland&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;GreeterEnvironment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;QT_WAYLAND_SHELL_INTEGRATION=layer-shell&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;[Wayland]&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;CompositorCommand&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;kwin_wayland --drm --no-lockscreen --no-global-shortcuts --locale1 --inputmethod maliit-keyboard&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Abrir &lt;code&gt;Preferencias &amp;gt; Pantalla de inicio de sesión (SDDM)&lt;/code&gt;, seleccionar Brisa como theme y pulsar el botón &lt;code&gt;Aplicar las preferencias de Plasma...&lt;/code&gt; arriba a la derecha.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;configuracion-sonido&#34;&gt;Configuración sonido&lt;a class=&#34;headerlink&#34; href=&#34;#configuracion-sonido&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Tal y como &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck#Audio&#34;&gt;comenta el wiki de Arch&lt;/a&gt;, el volumen por defecto en la Steam Deck es muy bajo. Para solucionarlo hay que instalar el paquete &lt;code&gt;alsa-utils&lt;/code&gt; y ejecutar el comando &lt;code&gt;alsamixer&lt;/code&gt; para ajustar el volumen de algunos de los canales.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsa-utils$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsamixer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Para acceder a los mismos, una vez en &lt;code&gt;alsamixer&lt;/code&gt;, hay que pulsar &lt;code&gt;F6&lt;/code&gt; para poder seleccionar el dispositivo de audio &lt;code&gt;acp5x&lt;/code&gt; ya que por defecto aparecen seleccionado el dispositivo 0 que es el asociado a la salida HDMI del chip gráfico:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;alsamixer&#34; src=&#34;images/posts/2024-09-04_steam_deck_arch/alsamixer-select-device.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Una vez seleccionado el dispositivo correcto, veremos todos sus canales que son muy numerosos. Localizaremos los que &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck#Audio&#34;&gt;menciona el wiki&lt;/a&gt; para ajustarlos a los siguientes valores:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Canal&lt;/th&gt;&lt;th style=&#34;text-align: right;&#34;&gt;Valor&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Digital&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;86%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Left Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Left Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Right Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Right Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&#34;instalacion-pamac&#34;&gt;Instalación pamac&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-pamac&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para facilitar la instalación de paquetes desde la interfaz gráfica, vamos a instalar &lt;code&gt;pamac&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pamac-aur&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Una vez instalado lo abrimos (buscando &lt;code&gt;Añadir/Quitar software&lt;/code&gt; en el cajón de aplicaciones) y activamos el soporte AUR en &lt;code&gt;Preferencias &amp;gt; Terceros &amp;gt; Activar soporte de AUR&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;instalacion-gestor-dualboot&#34;&gt;Instalación gestor dualboot&lt;a class=&#34;headerlink&#34; href=&#34;#instalacion-gestor-dualboot&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;El sistema Arch ya está listo y podemos arrancarlo encendiendo la consola en modo Boot Manager (manteniendo pulsada la tecla de bajar volumen) y seleccionando la opción &lt;code&gt;Arch&lt;/code&gt;. Pero para facilitar el arranque de SteamOS, vamos a instalar un gestor de arranque dualboot llamado &lt;a href=&#34;https://github.com/jlobue10/SteamDeck_rEFInd&#34;&gt;rEFInd&lt;/a&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Arrancar SteamOS (con Volume- + Power).&lt;/li&gt;&lt;li&gt;&lt;p&gt;Desde una terminal en modo escritorio ejecutar:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-rf&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/SteamDeck_rEFInd/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/jlobue10/SteamDeck_rEFInd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;SteamDeck_rEFInd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;chmod&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+x&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install-GUI.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./install-GUI.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Descargar o crear un icono de Arch Linux de 128x128px.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Abrir el lanzador del escritorio que encontraremos en el escritorio.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configurar de la siguiente forma (seleccionar en el campo &lt;code&gt;Boot Option #2 Icon&lt;/code&gt; el icono descargado o creado anteriormente):&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Configuración de rEFInd&#34; src=&#34;images/posts/2024-09-04_steam_deck_arch/rEFInd_conf.png&#34; /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pulsar botón &lt;code&gt;Create Config&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Editar el fichero que encontraremos en &lt;code&gt;~/.local/SteamDeck_rEFInd/GUI/refind.conf&lt;/code&gt; y modificar la última entrada de la siguiente forma:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;menuentry &amp;quot;Arch&amp;quot; { icon /EFI/refind/os_icon2.png loader /EFI/Arch/grubx64.efi graphics on}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Pulsar el botón &lt;code&gt;Install rEFInd&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Pulsar el botón &lt;code&gt;Install Config&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;La ruta &lt;code&gt;/EFI/Arch/grubx64.efi&lt;/code&gt; del paso 6 puede cambiar si en el comando &lt;code&gt;grub-install&lt;/code&gt; ejecutado durante la &lt;a href=&#34;#instalacion-base&#34;&gt;instalación del sistema base&lt;/a&gt; se eligió otro identificador que no fuera &lt;code&gt;Arch&lt;/code&gt;. En ese caso habría que adaptar la ruta.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusión&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Y esto sería todo. Ahora tenemos un sistema Arch Linux en nuestra Steam Deck con un gestor de arranque dualboot que nos permite arrancar también SteamOS. A partir de aquí tendremos una máquina con una naturaleza doble, una (SteamOS) para jugar y otra (Arch) para ser utilizada como un PC (Plasma) o tabletPC (Plasma Mobile).&lt;/p&gt;&lt;p&gt;Para elegir el entorno gráfico Plasma normal o el Plasma Mobile, utilizaremos el menú desplegable que aparece abajo a la izquierda en el gestor de inicios de sesión SDDM.&lt;/p&gt;&lt;iframe width=&#34;688&#34; height=&#34;387&#34; src=&#34;https://www.youtube.com/embed/hColcI3rv38&#34; title=&#34;Arch Linux en Steam Deck&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;Algunos enlaces de interés consultados durante la elaboración de esta guía:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.freecodecamp.org/news/how-to-install-arch-linux/&#34;&gt;The Arch Linux Handbook – Learn Arch Linux for Beginners&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://asgardius.company/?p=1783&#34;&gt;Instalando Arch Linux con KDE en Steam Deck&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck&#34;&gt;Steam Deck - ArchWiki&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>https://apuntes.eduardofilo.es/2024-09-04_steam_deck_arch.html</link> <pubDate>Wed, 04 Sep 2024 15:30:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/2024-09-04_steam_deck_arch.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2024-09-04_steam_deck_arch/steam_deck_logo.png" type="image/png" length="55688" /> </item> <item> <title>Installing Arch Linux on Steam Deck</title> <description>&lt;p&gt;&lt;img alt=&#34;Steam Deck with Arch Linux&#34; src=&#34;../images/posts/2024-09-04_steam_deck_arch/steam_deck_logo.png&#34; /&gt;&lt;/p&gt;&lt;div class=&#34;admonition info&#34;&gt;&lt;p class=&#34;admonition-title&#34;&gt;Loss of dual boot after updating SteamOS&lt;/p&gt;&lt;p&gt;In the update of SteamOS to the 3.6 branch (specifically, the first stable version of that branch was 3.6.19), the dual-boot configuration was lost, so the machine would only boot into SteamOS. The solution was to follow the steps in &lt;a href=&#34;#dual-boot-manager-installation&#34;&gt;Dual Boot manager installation&lt;/a&gt; starting from step 4.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;In July 2024, the 512GB LCD model of the Steam Deck was available for under €400 for a few weeks. This price was very appealing for a &lt;a href=&#34;https://www.steamdeck.com/es/tech/deck&#34;&gt;decent machine&lt;/a&gt; with an AMD processor based on x86 (AMD64) architecture, 16GB of RAM, an NVMe SSD of the mentioned capacity, a touchscreen, and integrated gaming controls. Essentially, it&#39;s a handheld PC, but still a PC. What makes this machine especially attractive to me is its full Linux support, making it an ideal platform for tinkering with Linux distributions or using it as a secondary portable PC. Of course, this is in addition to its conventional use for gaming and &lt;a href=&#34;https://www.emudeck.com/&#34;&gt;emulating&lt;/a&gt; video games.&lt;/p&gt;&lt;p&gt;SteamOS, the default operating system, is based on Arch Linux, and in desktop mode, it can be used almost like a regular Arch Linux system. The main restriction is that it&#39;s built in immutable mode. While this type of system configuration has its virtues and it&#39;s understandable why Valve chose it for their machine, I still prefer a normal (mutable) system. So, I decided to install a regular Arch Linux and learn how to install this distribution. According to the &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck&#34;&gt;Arch wiki&lt;/a&gt;, the hardware of the Steam Deck LCD model is 100% supported by Arch.&lt;/p&gt;&lt;p&gt;The following is the complete procedure I used.&lt;/p&gt;&lt;h2 id=&#34;keyboardmouse&#34;&gt;Keyboard/Mouse&lt;a class=&#34;headerlink&#34; href=&#34;#keyboardmouse&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Although most operations can be performed using the Steam Deck&#39;s touchscreen and trackpads, it&#39;s highly recommended to connect a keyboard and mouse to the console to facilitate the installation, especially during the terminal session where we&#39;ll install the base system. The Steam Deck only has one USB-C port, so the most practical solution is to use a dock. It may also suffice (especially if we run live systems from a microSD) to use a wireless keyboard/mouse dongle connected with a USB&amp;lt;-&amp;gt;USB-C adapter, though in that case, it&#39;s advisable to ensure the Steam Deck is fully charged.&lt;/p&gt;&lt;p&gt;In my case, I used a Dell &lt;a href=&#34;https://www.dell.com/es-es/shop/adaptador-multipuerto-usb-c-de-dell-7-en-1-da310/apd/470-aeup/conexi%C3%B3n-wifi-y-redes&#34;&gt;DA310z&lt;/a&gt; dock.&lt;/p&gt;&lt;h2 id=&#34;disk-repartitioning&#34;&gt;Disk repartitioning&lt;a class=&#34;headerlink&#34; href=&#34;#disk-repartitioning&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The Steam Deck&#39;s SSD I purchased is 512GB and of the NVMe type. This is the minimum size if we want to install another system alongside SteamOS.&lt;/p&gt;&lt;p&gt;Before starting the installation, we need to make space by shrinking the main partition (number 8). For this, we can use almost any live Linux distribution that we boot from a removable drive (USB or microSD) since all of them come with some partition management program. I used a &lt;code&gt;gparted&lt;/code&gt; ISO.&lt;/p&gt;&lt;p&gt;The complete procedure can be seen below:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Download the &lt;a href=&#34;https://gparted.org/download.php&#34;&gt;gparted ISO&lt;/a&gt;. Specifically, I used &lt;a href=&#34;https://downloads.sourceforge.net/gparted/gparted-live-1.6.0-3-amd64.iso&#34;&gt;this version&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Install the ISO on a USB drive or, even better, on a microSD (since the Steam Deck has a slot for these). You can use a program like &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; or the &lt;code&gt;dd&lt;/code&gt; command in Linux if you &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;know how to use it&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Boot the Steam Deck with the USB drive or microSD inserted by holding down the volume- button until you hear the power-up tones. This causes the console to boot into the &lt;code&gt;Boot Manager&lt;/code&gt; menu.&lt;/li&gt;&lt;li&gt;Select the option to boot from the removable device, which will appear in the menu as &lt;code&gt;EFI USB Device (USB)&lt;/code&gt; or &lt;code&gt;EFI SD/MMC Card (XX XXXX XXXX)&lt;/code&gt; depending on whether you used a USB drive or a microSD respectively.&lt;/li&gt;&lt;li&gt;&lt;p&gt;A configuration screen appears where we make the following selections:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Policy for handling keymaps =&amp;gt; Don&#39;t touch keymap&lt;/li&gt;&lt;li&gt;Which language do you prefer? =&amp;gt; 25&lt;/li&gt;&lt;li&gt;Which mode do you prefer? =&amp;gt; 0&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;After accepting the last option, the &lt;code&gt;gparted&lt;/code&gt; program will start in graphical mode (with the screen rotated, though). We will make changes until the partitions look like the screenshot. Essentially, we&#39;ll shrink partition #8 by about 100GB, create #9 with 4GB for swap, and #10 with the remaining space for the Arch root system:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;Steam Deck Partitions&#34; src=&#34;../images/posts/2024-09-04_steam_deck_arch/steam-deck-partitions.png&#34; /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Note, when looking at the partitions used by SteamOS, that it uses an &lt;a href=&#34;https://blog.davidbyrne.dev/2018/08/16/linux-ab-partitions&#34;&gt;A/B partition system&lt;/a&gt;, common in Android, where most of the system partitions (except the user space one) are duplicated. This system is designed to facilitate updates, or rather to roll back in case of problems during them.&lt;/p&gt;&lt;h2 id=&#34;base-installation&#34;&gt;Base installation&lt;a class=&#34;headerlink&#34; href=&#34;#base-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For the base system installation, I opted for the conventional method following the instructions in the &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;installation guide&lt;/a&gt; on the Arch wiki. This installation uses an Arch system ISO that we can boot from a removable drive (USB or microSD) to install the basic packages needed for a bootable and very basic system.&lt;/p&gt;&lt;p&gt;Before starting, note that due to the dynamic nature of the Arch Linux distribution, it&#39;s very likely that this guide will become outdated soon. Therefore, I recommend checking the &lt;a href=&#34;https://wiki.archlinux.org/title/Installation_guide&#34;&gt;official guide&lt;/a&gt; if something doesn&#39;t go as expected.&lt;/p&gt;&lt;p&gt;Below is the step-by-step installation process:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Download the &lt;a href=&#34;https://archlinux.org/download/&#34;&gt;Arch Linux ISO&lt;/a&gt;. Specifically, I used &lt;a href=&#34;https://es.mirrors.cicku.me/archlinux/iso/2024.08.01/archlinux-2024.08.01-x86_64.iso&#34;&gt;this version&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Install the ISO on a USB drive or, even better, on a microSD to use the slot on the Steam Deck. You can use a program like &lt;a href=&#34;https://www.balena.io/etcher/&#34;&gt;Balena Etcher&lt;/a&gt; or the &lt;code&gt;dd&lt;/code&gt; command in Linux if you &lt;a href=&#34;sistemas/raspi.html#backup-de-la-sd-comprimiendo-al-vuelo&#34;&gt;know how to use it&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Boot the Steam Deck with the USB drive or microSD inserted by holding down the volume- button until you hear the power-up tones. This causes the console to boot into the &lt;code&gt;Boot Manager&lt;/code&gt; menu.&lt;/li&gt;&lt;li&gt;Select the option to boot from the removable device, which will appear in the menu as &lt;code&gt;EFI USB Device (USB)&lt;/code&gt; or &lt;code&gt;EFI SD/MMC Card (XX XXXX XXXX)&lt;/code&gt; depending on whether you used a USB drive or a microSD respectively.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once the Arch live installation system finishes booting, run the following commands in order (in the &lt;code&gt;iwctl&lt;/code&gt; session, replace &lt;code&gt;&amp;lt;SSID&amp;gt;&lt;/code&gt; with your own and enter the password when prompted; also replace the user identifier &lt;code&gt;&amp;lt;USER&amp;gt;&lt;/code&gt; with the one you want to use):&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;34&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# loadkeys en&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# iwctl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 scan&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 get-networks&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# station wlan0 connect &amp;lt;SSID&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;iwd&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mkfs.ext4 /dev/nvme0n1p10&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mkswap /dev/nvme0n1p9&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mount /dev/nvme0n1p10 /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# mount --mkdir /dev/nvme0n1p1 /mnt/boot/efi&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# swapon /dev/nvme0n1p9&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacstrap -K /mnt base base-devel linux linux-firmware sudo vi ntfs-3g networkmanager amd-ucode grub efibootmgr git cmake qt5-wayland&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# genfstab -U /mnt &amp;gt;&amp;gt; /mnt/etc/fstab&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# arch-chroot /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# hwclock --systohc&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# vi /etc/locale.gen # uncomment &amp;#39;en_US.UTF-8 UTF-8&amp;#39;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# locale-gen&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;LANG=en_US.UTF-8&amp;quot; &amp;gt; /etc/locale.conf&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;KEYMAP=en&amp;quot; &amp;gt; /etc/vconsole.conf&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo &amp;quot;deck&amp;quot; &amp;gt; /etc/hostname&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# echo -e &amp;quot;\n127.0.0.1 localhost\n::1 localhost\n127.0.1.1 deck&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# passwd&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Arch&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# grub-mkconfig -o /boot/grub/grub.cfg&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# systemctl enable NetworkManager&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# useradd -m -G wheel,audio,video,storage &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# passwd &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# chmod u+w /etc/sudoers&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# vi /etc/sudoers # Uncomment &amp;#39;%wheel ALL=(ALL) ALL&amp;#39;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# chmod u-w /etc/sudoers&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacman -Syy&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pacman -S xorg-server xf86-video-amdgpu maliit-keyboard qt5-wayland&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# su - &amp;lt;USER&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The password you choose when running the &lt;code&gt;passwd &amp;lt;USER&amp;gt;&lt;/code&gt; command should be numeric if you want to use Plasma Mobile, as the unlock screen only allows you to enter a PIN.&lt;/p&gt;&lt;h2 id=&#34;graphical-environment-installation&#34;&gt;Graphical environment installation&lt;a class=&#34;headerlink&#34; href=&#34;#graphical-environment-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuing from where we left off, that is, in the chroot session from the Arch Linux live installation system. We will now do this as a normal user (not root), so from now on, we will frequently use &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now we are going to install the graphical environments. I say this in plural because we will install two: Plasma and Plasma Mobile. I do this because, depending on whether we will use the Steam Deck with Arch in desktop mode (with keyboard/mouse and possibly a screen) or portable mode (without keyboard/mouse), one system or the other will be more convenient, as Plasma Mobile has an interface designed to be operated with a touchscreen (tablet-style).&lt;/p&gt;&lt;p&gt;Really, it&#39;s not necessary to install both desktops, so their installation steps are separated. You can install one, the other, or both.&lt;/p&gt;&lt;h4 id=&#34;plasma&#34;&gt;Plasma&lt;a class=&#34;headerlink&#34; href=&#34;#plasma&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-meta&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;During the installation of the above package, you will be asked several questions. On the third question, answer &lt;code&gt;2) noto-fonts&lt;/code&gt;. For the rest, provide the default answer (by pressing enter).&lt;/p&gt;&lt;h4 id=&#34;plasma-mobile&#34;&gt;Plasma Mobile&lt;a class=&#34;headerlink&#34; href=&#34;#plasma-mobile&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Plasma Mobile is not yet available in the official repositories. We have to use &lt;a href=&#34;https://aur.archlinux.org/&#34;&gt;AUR&lt;/a&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Start by installing some dependencies:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-workspace&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kcontacts&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kirigami2&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kpeople&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;libphonenumber&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Install the AUR packages &lt;code&gt;plasma-nano&lt;/code&gt;, &lt;code&gt;plasma-settings&lt;/code&gt;, and &lt;code&gt;plasma-mobile&lt;/code&gt; in this order:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 6&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 7&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 8&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt; 9&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;12&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-nano.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-nano$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-settings.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-settings$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;~$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://aur.archlinux.org/plasma-mobile.git$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;plasma-mobile$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;makepkg&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-si&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h4 id=&#34;end-of-graphical-environment-installation&#34;&gt;End of graphical environment installation&lt;a class=&#34;headerlink&#34; href=&#34;#end-of-graphical-environment-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Install the SDDM session manager and some applications, which we&#39;ll need to finish the installation from the graphical mode:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sddm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;konsole&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;dolphin&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kwrite&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;partitionmanager&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;power-profiles-daemon$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sddm$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Close the chroot session, unmount the partitions, and reboot the system:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;c1&#34;&gt;# exit&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# umount -R /mnt&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# reboot&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;basic-configuration&#34;&gt;Basic configuration&lt;a class=&#34;headerlink&#34; href=&#34;#basic-configuration&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After rebooting, you should see the SDDM session manager, initially with the screen rotated and without the ability to use the on-screen keyboard, so we&#39;ll still be using the external keyboard and mouse.&lt;/p&gt;&lt;p&gt;Once we have logged into Plasma or Plasma Mobile, the first thing we need to do is configure the internet connection. To do this, click on the corresponding icon in the system tray at the bottom right in Plasma or at the top right in Plasma Mobile.&lt;/p&gt;&lt;p&gt;Once connected, open the Konsole application and run the following commands to install the basic KDE application package, Firefox browser and the SSH server:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;firefox&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;kde-applications-meta&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;openssh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;usbutils&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;lshw&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;man-db&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;htop&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;spectacle$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;start&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;systemctl&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sshd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Finally, we make the following configurations:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Preferences &amp;gt; System Preferences &amp;gt; Input &amp;amp; Output &amp;gt; Display &amp;amp; Monitor &amp;gt; Scale&lt;/code&gt;: Set the screen scale to 110%.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferences &amp;gt; System Preferences &amp;gt; Input &amp;amp; Output &amp;gt; Keyboard &amp;gt; Layouts &amp;gt; Configure Layouts &amp;gt; Add&lt;/code&gt;: Add the English keyboard layout.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferences &amp;gt; System Preferences &amp;gt; Input &amp;amp; Output &amp;gt; Keyboard &amp;gt; Virtual Keyboard&lt;/code&gt;: Select &lt;code&gt;Maliit&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferences &amp;gt; System Preferences &amp;gt; Language &amp;amp; Time &amp;gt; Date &amp;amp; Time &amp;gt; Time Zone&lt;/code&gt;: Select your city.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Preferences &amp;gt; System Preferences &amp;gt; Appearance &amp;amp; Style &amp;gt; Text &amp;amp; Fonts &amp;gt; Fonts&lt;/code&gt;. Set this values:&lt;ul&gt;&lt;li&gt;&lt;code&gt;General&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Fixed width&lt;/code&gt;: &lt;code&gt;Hack 10pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Small&lt;/code&gt;: &lt;code&gt;Noto Sans 9pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Toolbar&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Menu&lt;/code&gt;: &lt;code&gt;Noto Sans 11pt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Window title&lt;/code&gt;: &lt;code&gt;Noto Sans 10pt&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Right-click (L2) on the taskbar and select &lt;code&gt;Show panel settings&lt;/code&gt;. In &lt;code&gt;Panel Preferences &amp;gt; Panel Height&lt;/code&gt; increase the height to 54px.&lt;/li&gt;&lt;li&gt;Right-click (L2) on the icon used to show hidden icons in the system tray and select &lt;code&gt;Configure System Tray&lt;/code&gt;. In &lt;code&gt;General&lt;/code&gt; select these values:&lt;ul&gt;&lt;li&gt;&lt;code&gt;Panel icon size&lt;/code&gt;: &lt;code&gt;Scale to panel height&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Spacing between panel icons&lt;/code&gt;: &lt;code&gt;Normal&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Right-click (L2) on the icon used to show hidden icons in the system tray and select &lt;code&gt;Show panel settings&lt;/code&gt;. You will see a small clock that was not visible before, right-click (L2) on it. Select &lt;code&gt;Show alternatives&lt;/code&gt; and from the alternative graphics that appear, choose &lt;code&gt;Digital clock&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&#34;sddm-configuration&#34;&gt;SDDM configuration&lt;a class=&#34;headerlink&#34; href=&#34;#sddm-configuration&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Finally, we&#39;ll improve the SDDM session manager configuration to enable the on-screen keyboard, enhance the appearance, and display the screen in the correct orientation:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Create the file &lt;code&gt;/etc/sddm.conf.d/10-wayland.conf&lt;/code&gt; with the following content:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;[General]&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;DisplayServer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;wayland&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;GreeterEnvironment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;QT_WAYLAND_SHELL_INTEGRATION=layer-shell&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;[Wayland]&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;CompositorCommand&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;kwin_wayland --drm --no-lockscreen --no-global-shortcuts --locale1 --inputmethod maliit-keyboard&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open &lt;code&gt;Preferences &amp;gt; Login Screen (SDDM)&lt;/code&gt;, select Breeze as the theme, and click the &lt;code&gt;Apply Plasma settings...&lt;/code&gt; button in the top right corner.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&#34;sound-configuration&#34;&gt;Sound configuration&lt;a class=&#34;headerlink&#34; href=&#34;#sound-configuration&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck#Audio&#34;&gt;the Arch wiki mentions&lt;/a&gt;, the default volume on the Steam Deck is very low. To fix this, you need to install the &lt;code&gt;alsa-utils&lt;/code&gt; package and run the &lt;code&gt;alsamixer&lt;/code&gt; command to adjust the volume of some of the channels.&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;sudo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pacman&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsa-utils$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;alsamixer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;To access them, once in &lt;code&gt;alsamixer&lt;/code&gt;, press &lt;code&gt;F6&lt;/code&gt; to select the &lt;code&gt;acp5x&lt;/code&gt; audio device, as by default the selected device is 0, which is associated with the HDMI output of the graphics chip:&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;alsamixer&#34; src=&#34;../images/posts/2024-09-04_steam_deck_arch/alsamixer-select-device.png&#34; /&gt;&lt;/p&gt;&lt;p&gt;Once the correct device is selected, you will see all its channels, which are quite numerous. Locate the ones &lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck#Audio&#34;&gt;mentioned in the wiki&lt;/a&gt; and adjust them to the following values:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&#34;text-align: left;&#34;&gt;Channel&lt;/th&gt;&lt;th style=&#34;text-align: right;&#34;&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Digital&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;86%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Left Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Left Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Right Analog PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#34;text-align: left;&#34;&gt;Right Digital PCM&lt;/td&gt;&lt;td style=&#34;text-align: right;&#34;&gt;85%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h2 id=&#34;pamac-installation&#34;&gt;Pamac Installation&lt;a class=&#34;headerlink&#34; href=&#34;#pamac-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To facilitate package installation from the graphical interface, we are going to install &lt;code&gt;pamac&lt;/code&gt;:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;yay&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-S&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;pamac-aur&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Once installed, open it (by searching for &lt;code&gt;Add/Remove Software&lt;/code&gt; in the application drawer) and enable AUR support in &lt;code&gt;Preferences &amp;gt; Third Party &amp;gt; Enable AUR support&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&#34;dual-boot-manager-installation&#34;&gt;Dual Boot manager installation&lt;a class=&#34;headerlink&#34; href=&#34;#dual-boot-manager-installation&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The Arch system is now ready and can be booted by starting the console in Boot Manager mode (holding down the volume- key) and selecting the &lt;code&gt;Arch&lt;/code&gt; option. But to make it easier to boot SteamOS, we&#39;ll install a dual boot manager called &lt;a href=&#34;https://github.com/jlobue10/SteamDeck_rEFInd&#34;&gt;rEFInd&lt;/a&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Boot SteamOS (with Volume- + Power).&lt;/li&gt;&lt;li&gt;&lt;p&gt;From a terminal in desktop mode, run:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;rm&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-rf&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$HOME&lt;/span&gt;/SteamDeck_rEFInd/&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;git&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;clone&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;https://github.com/jlobue10/SteamDeck_rEFInd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;SteamDeck_rEFInd&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;chmod&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;+x&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;install-GUI.sh&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;./install-GUI.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Download or create an Arch Linux icon of 128x128px.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Open the desktop launcher found on the desktop.&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure it as follows (select the icon downloaded or created earlier in the &lt;code&gt;Boot Option #2 Icon&lt;/code&gt; field):&lt;/p&gt;&lt;p&gt;&lt;img alt=&#34;rEFInd Configuration&#34; src=&#34;../images/posts/2024-09-04_steam_deck_arch/rEFInd_conf.png&#34; /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the &lt;code&gt;Create Config&lt;/code&gt; button.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Edit the file found at &lt;code&gt;~/.local/SteamDeck_rEFInd/GUI/refind.conf&lt;/code&gt; and modify the last entry as follows:&lt;/p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;table class=&#34;highlighttable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;linenos&#34;&gt;&lt;div class=&#34;linenodiv&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;normal&#34;&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;menuentry &amp;quot;Arch&amp;quot; { icon /EFI/refind/os_icon2.png loader /EFI/Arch/grubx64.efi graphics on}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the &lt;code&gt;Install rEFInd&lt;/code&gt; button.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;code&gt;Install Config&lt;/code&gt; button.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The path &lt;code&gt;/EFI/Arch/grubx64.efi&lt;/code&gt; in step 6 may change if you chose a different identifier than &lt;code&gt;Arch&lt;/code&gt; during the &lt;code&gt;grub-install&lt;/code&gt; command executed during the &lt;a href=&#34;#base-installation&#34;&gt;base system installation&lt;/a&gt;. In that case, you&#39;ll need to adapt the path.&lt;/p&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;a class=&#34;headerlink&#34; href=&#34;#conclusion&#34; title=&#34;Permanent link&#34;&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;And that&#39;s it. Now we have an Arch Linux system on our Steam Deck with a dual boot manager that allows us to boot into SteamOS as well. From here, we have a machine with a dual nature, one (SteamOS) for gaming and another (Arch) to be used as a PC (Plasma) or tablet PC (Plasma Mobile).&lt;/p&gt;&lt;p&gt;To choose between the normal Plasma or Plasma Mobile graphical environment, use the dropdown menu that appears at the bottom left in the SDDM login manager.&lt;/p&gt;&lt;iframe width=&#34;688&#34; height=&#34;387&#34; src=&#34;https://www.youtube.com/embed/hColcI3rv38&#34; title=&#34;Arch Linux on Steam Deck&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;Some interesting links consulted during the creation of this guide:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.freecodecamp.org/news/how-to-install-arch-linux/&#34;&gt;The Arch Linux Handbook – Learn Arch Linux for Beginners&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://asgardius.company/?p=1783&#34;&gt;Instalando Arch Linux con KDE en Steam Deck&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/title/Steam_Deck&#34;&gt;Steam Deck - ArchWiki&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>https://apuntes.eduardofilo.es/en/2024-09-04_steam_deck_arch.html</link> <pubDate>Wed, 04 Sep 2024 15:30:00 +0200</pubDate><source url="https://apuntes.eduardofilo.es/rss.xml">Apuntes de eduardófilo</source><guid isPermaLink="true">https://apuntes.eduardofilo.es/en/2024-09-04_steam_deck_arch.html</guid> <enclosure url="https://apuntes.eduardofilo.es/images/posts/2024-09-04_steam_deck_arch/steam_deck_logo.png" type="image/png" length="55688" /> </item> </channel></rss>