<?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: Juan Torchia</title>
    <description>The latest articles on DEV Community by Juan Torchia (@jtorchia).</description>
    <link>https://dev.to/jtorchia</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%2F885942%2F099b05dc-1940-49f6-a022-9c6a392bb405.jpg</url>
      <title>DEV Community: Juan Torchia</title>
      <link>https://dev.to/jtorchia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jtorchia"/>
    <language>en</language>
    <item>
      <title>I ran OpenAI's security audit 10 times: to review your own code, plain ChatGPT is enough</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:36:20 +0000</pubDate>
      <link>https://dev.to/jtorchia/i-ran-openais-security-audit-10-times-to-review-your-own-code-plain-chatgpt-is-enough-2gd3</link>
      <guid>https://dev.to/jtorchia/i-ran-openais-security-audit-10-times-to-review-your-own-code-plain-chatgpt-is-enough-2gd3</guid>
      <description>&lt;p&gt;There's a pattern that keeps showing up on the timeline: someone vibecodes an app over a weekend, ships it, it does well, and a week later they're left with the nagging question — "is this thing secure?" And right on cue, in comes OpenAI's pitch: Daybreak Blue, the defensive lane of their cybersecurity stack, "for secure code review, vulnerability discovery, and patch validation." Which is, exactly, what that dev needs.&lt;/p&gt;

&lt;p&gt;I have access to Blue. So instead of taking the announcement at its word, I ran the experiment: I grabbed a real app, handed it the &lt;strong&gt;same request, word for word&lt;/strong&gt;, to plain ChatGPT and to Blue, and compared. The question this answers for anyone who vibecodes is simple: to secure your app, does the special tier do anything that regular ChatGPT doesn't?&lt;/p&gt;

&lt;p&gt;I ran it once and got a clean, shareable answer. I almost published it. Good thing I ran it nine more times.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Blue" is, in plain terms
&lt;/h2&gt;

&lt;p&gt;Daybreak &lt;strong&gt;Blue&lt;/strong&gt; isn't a different model: it runs on the same base model (Sol) with the cybersecurity safeguards loosened for authorized work. The one that &lt;em&gt;does&lt;/em&gt; ship a separate model is the other tier, Red —GPT-5.6-Cyber—, which isn't available to individuals. In the Codex build I was granted, the program is picked with a flag, &lt;code&gt;--cyber-access-program&lt;/code&gt;, that I set to &lt;code&gt;standard&lt;/code&gt; (regular ChatGPT) or &lt;code&gt;daybreak_blue&lt;/code&gt;; it doesn't appear in OpenAI's public documentation, I know because it's the one I run. So I audited &lt;strong&gt;the same base model&lt;/strong&gt;, turning &lt;strong&gt;only&lt;/strong&gt; that one knob. That's the only variable —at least the only one OpenAI says changes: from the outside I can't audit that Blue doesn't also touch the system prompt or the scaffold, so I take it for what it is, the vendor's word—.&lt;/p&gt;

&lt;p&gt;And heads up, this changes what &lt;em&gt;you&lt;/em&gt; can reproduce: you don't get into Blue by ticking a box. You need a paid plan with Advanced Account Security, a registered physical FIDO2 key —yes, a YubiKey; that was the first post in this series—, identity verification with a selfie and ID at &lt;code&gt;chatgpt.com/cyber&lt;/code&gt;, and a justification for what you'll use it for. OpenAI reviews case by case and can turn you down: completing the setup doesn't guarantee access (&lt;a href="https://help.openai.com/en/articles/20001258-openai-daybreak-trusted-access-for-cyber-overview" rel="noopener noreferrer"&gt;the official process, here&lt;/a&gt;). So in this experiment you can reproduce the &lt;code&gt;standard&lt;/code&gt; side —that's the ChatGPT you already have— but not necessarily the &lt;code&gt;daybreak_blue&lt;/code&gt; one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup (reproducible)
&lt;/h2&gt;

&lt;p&gt;Target: &lt;a href="https://github.com/anchorcms/anchor-cms" rel="noopener noreferrer"&gt;anchor-cms&lt;/a&gt;, a PHP CMS, 3,300 stars, in real use but abandoned since 2018. Router, auth, and database layer all hand-rolled —the place where real bugs live, and also the easiest possible case for any model: old PHP, no framework covering anything up—.&lt;/p&gt;

&lt;p&gt;Same prompt for both, read-only sandbox (it reads the code, no network, no running the app), same machine, same commit. Here's the exact prompt, in case you want to run it on your own app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a security analyst doing defensive code review on this
repository. Work by reading the code only; do not access the network
or run the app.
1) Threat modeling: what untrusted input it processes and where it enters.
2) Concrete vulnerabilities: type, file:line, why it's exploitable
   (chain from the entry point to the sink), severity.
3) Rank them by evidence and impact.
4) For the most severe ones, propose the patch.
Be honest: if something looks vulnerable but is mitigated, say so. Do
not invent findings to fill the list. Always cite file:line.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(I ask both for "don't invent," but I'm not kidding myself: asking a model to be honest guarantees nothing. What filters out the filler is that &lt;em&gt;I&lt;/em&gt; verify every finding against the code, not the prompt's good intentions.)&lt;/p&gt;

&lt;p&gt;And how do you hand it your repo? Simplest route: upload the files to a ChatGPT or Claude Project, or use an agent tool (Codex, Cursor, Claude Code) that already sees your code, and paste the prompt above.&lt;/p&gt;

&lt;h2&gt;
  
  
  First surprise: neither one refused
&lt;/h2&gt;

&lt;p&gt;A good chunk of the Daybreak story rests on one idea: the normal model has brakes for security work, and Blue takes them off. OpenAI even publishes the number. And here's the detail almost nobody reads: that number —the general model completes &lt;strong&gt;1.5%&lt;/strong&gt; of the requests, Blue &lt;strong&gt;2.0%&lt;/strong&gt;— is about &lt;strong&gt;offensive&lt;/strong&gt; tasks: exploit-chain development, authentication bypass, privilege escalation &lt;em&gt;to attack&lt;/em&gt; (&lt;a href="https://www.infosecurity-magazine.com/news/openai-daybreak-blue-red-gpt-cyber/" rel="noopener noreferrer"&gt;Infosecurity Magazine&lt;/a&gt;). The big jump (95%) belongs to a different model, GPT-5.6-Cyber, which only the Red tier gives you.&lt;/p&gt;

&lt;p&gt;Reviewing &lt;strong&gt;your own code&lt;/strong&gt; to protect it is none of those things. And it showed: all ten runs, standard and Blue, did the complete audit without a single "I can't help you with this." The brake never kicked in because my task was never in the category the brake blocks. That alone answers half the post: to secure your app, the ChatGPT you have doesn't balk.&lt;/p&gt;

&lt;h2&gt;
  
  
  What both of them found, every time
&lt;/h2&gt;

&lt;p&gt;I verified it by reading the code, not taking its word —chains that are plausible per the code, not proven in execution: I never ran an exploit—. Across the ten runs, the three serious bugs showed up without fail:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privilege escalation.&lt;/strong&gt; A low-privilege user becomes an administrator. In the edit-user route there is, literally, this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Force admin for now&lt;/span&gt;
&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'administrator'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The authorization filter only checks that a session exists, never the role. You edit your own profile and come out an admin. With one caveat the reports flag and that's worth stating: the default anchor-cms install also forces &lt;code&gt;administrator&lt;/code&gt; when &lt;strong&gt;creating&lt;/strong&gt; users, so out of the box there are no low-privilege accounts to escalate from. The bug bites in migrated installs, with imported data or a manual seed, where those accounts do exist. Cataloged as &lt;a href="https://app.opencve.io/cve/CVE-2026-88959" rel="noopener noreferrer"&gt;CVE-2026-88959&lt;/a&gt; (CVSS 8.7 per the registry), published a month before this experiment —after both models' training cutoff, so they found it by reading, not remembering—.&lt;/p&gt;

&lt;p&gt;The other two constants: &lt;strong&gt;account takeover via the &lt;code&gt;Host&lt;/code&gt; header&lt;/strong&gt; in the password reset (the link is built with &lt;code&gt;$_SERVER['HTTP_HOST']&lt;/code&gt; without validation) and &lt;strong&gt;CSRF via GET&lt;/strong&gt; (the protection only validates &lt;code&gt;POST&lt;/code&gt;, and all deletes are &lt;code&gt;GET&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;So far, that's what the first run gave me. Its conclusion: "Blue found a fourth critical the standard missed (the installer), the standard found two session bugs Blue missed. Near tie, slight edge to Blue in depth." Nice, balanced, false.&lt;/p&gt;

&lt;h2&gt;
  
  
  The twist: I ran it ten times
&lt;/h2&gt;

&lt;p&gt;An LLM isn't deterministic. Ask it the same thing twice and it answers differently. So before publishing "Blue found X and the standard didn't," I ran it &lt;strong&gt;five times per side&lt;/strong&gt; and counted in how many runs each finding appeared. These are two separate axes: how serious the bug is (verified against the code) and in how many runs each side caught it. Detection is noisy; severity doesn't change with it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Standard detection&lt;/th&gt;
&lt;th&gt;Blue detection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Privilege escalation (CVE, conditional)&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSRF via GET&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host-header in the reset&lt;/td&gt;
&lt;td&gt;Medium-high&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open installer → takeover&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;td&gt;2/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sessions / enumeration / error leakage&lt;/td&gt;
&lt;td&gt;Medium-low&lt;/td&gt;
&lt;td&gt;noise, both&lt;/td&gt;
&lt;td&gt;noise, both&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reflected XSS (borderline, disputed)&lt;/td&gt;
&lt;td&gt;Low/doubtful&lt;/td&gt;
&lt;td&gt;0/5&lt;/td&gt;
&lt;td&gt;3/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LFI via &lt;code&gt;pagetype&lt;/code&gt; (conditional)&lt;/td&gt;
&lt;td&gt;Low/conditional&lt;/td&gt;
&lt;td&gt;0/5&lt;/td&gt;
&lt;td&gt;2/5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The "installer critical" I'd pinned on Blue as an exclusive find &lt;strong&gt;is caught twice as often by the standard&lt;/strong&gt;: 4 of 5 runs versus Blue's 2 of 5. My comparison had it backwards out of pure sampling luck. If I'd published the first run, I'd have published a coincidence as if it were a finding.&lt;/p&gt;

&lt;p&gt;Enumeration, session fixation, and trace leakage show up in both models evenly: they're there, but they don't tell one apart from the other. The only two findings that come up 100% of the time on both sides are the privesc and the CSRF; the host-header lands in a stable tie, 4 of 5 on each. Where there's consistency, there's a tie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did Blue find anything the standard didn't? Not exactly
&lt;/h2&gt;

&lt;p&gt;What looks at first glance like Blue's edge: it reported two things the standard never listed as findings —a reflected XSS from "flashed" form values and a local file inclusion via the &lt;code&gt;pagetype&lt;/code&gt; field, which gets concatenated into a path without sanitizing—. Sounds like Blue saw something the standard missed. But when you read the raw reports, at least in the stronger case —the XSS— that's not what happened: the standard &lt;strong&gt;looked at the same code and dismissed it with an argument&lt;/strong&gt;. One run traces it down to the internal &lt;code&gt;Input::flash()&lt;/code&gt; mechanism and concludes, verbatim, that it "does not assert an exploitable XSS chain with the current code." With &lt;code&gt;pagetype&lt;/code&gt; it's different, and I have to be honest: the standard neither traced it nor dismissed it, it simply never raised it as a vulnerability in any of its five runs. There a sink it didn't get to may exist.&lt;/p&gt;

&lt;p&gt;In other words: on the XSS it's not a coverage gap (Blue found it, the standard didn't), it's a &lt;strong&gt;criterion disagreement&lt;/strong&gt; —Blue reports it as a finding; the standard analyzes it and judges it not exploitable—. On &lt;code&gt;pagetype&lt;/code&gt; the split is grayer, the standard may not have reached that sink. But in neither case are we looking at a bug that sinks you: the XSS is so borderline that Blue itself doesn't report it in 2 of every 5 runs, and &lt;code&gt;pagetype&lt;/code&gt; is a conditional, low-severity LFI. If the standard's judgment is the right one, Blue's reports are false positives; and even if it isn't, more &lt;em&gt;breadth&lt;/em&gt; on the borderline stuff isn't obviously better: for an auditor, a well-dismissed false positive is worth as much as a finding.&lt;/p&gt;

&lt;p&gt;And the statistics land in the same place. With five runs per side, a 3/5 versus 0/5 has a ~17% chance of coming out that way by pure chance even if the two tiers were identical (Fisher's exact test): nowhere near significant. This design could only prove a near-total difference —4 or 5 of 5 versus 0—, and the only thing that reaches that consistency is the serious bugs, which &lt;strong&gt;tie&lt;/strong&gt; (and on top of that, looking at thirteen distinct findings, having at least one come out this skewed is pure chance, not signal). A note on method: I counted a finding only if the report lists it as an actionable vulnerability, not if it mentions it to dismiss it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verdict, with the numbers in hand
&lt;/h2&gt;

&lt;p&gt;For the concrete question —"is the special tier worth it for securing my app?"— the answer is &lt;strong&gt;no, it barely changes anything&lt;/strong&gt;, and now I can say it with data instead of one lucky run. Both models found the bugs that sink you in all ten runs, without missing once. The specialized tier didn't add a single serious finding the standard missed: what it added was noise on the borderline. Nothing more.&lt;/p&gt;

&lt;p&gt;An honest limit before you generalize: this is &lt;strong&gt;one&lt;/strong&gt; app and &lt;strong&gt;one&lt;/strong&gt; prompt. The five runs kill the randomness &lt;em&gt;within&lt;/em&gt; that case, they don't promise you the same on your Go microservice or your app on a framework that already covers half of OWASP for you. anchor-cms is old PHP, hand-rolled, with no safety net: the most favorable case for &lt;em&gt;any&lt;/em&gt; model to shine. Read the conclusion as "for this kind of target," not as a universal law.&lt;/p&gt;

&lt;p&gt;And one more limit, this time on Blue's side: I deliberately picked the most harmless defensive task —read code, without running or writing an exploit—, which is exactly where the standard model's brake was never going to kick in. Blue is also sold for validating patches with a proof of concept, analyzing malware, or responding to incidents; several of those &lt;em&gt;do&lt;/em&gt; produce offensive artifacts and &lt;em&gt;do&lt;/em&gt; trigger refusals from the standard. There Blue could unlock what regular ChatGPT bounces back at you. I didn't test it: my experiment measures the sub-case where, by design, Blue had no way to win.&lt;/p&gt;

&lt;p&gt;The lessons, which aren't about which model you use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A capable model finds your app's serious bugs reliably.&lt;/strong&gt; Great… and it also means anyone with the same tool finds them just as fast. If you vibecode, assume your code will be audited by a model: do it yourself first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't run it just once.&lt;/strong&gt; This is the part that almost got me. One run is a single sample of a random variable. Run the same prompt three or four times and keep the union of the findings; half the useful stuff only shows up on the second or third pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify.&lt;/strong&gt; The most serious bug was a &lt;code&gt;// Force admin for now&lt;/code&gt; comment in plain sight, and even so each run missed different things. The model speeds you up; it doesn't replace your judgment. Ask for file and line, ask it to dismiss false positives, and follow the chain yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is against Daybreak. But watch the argument: the numbers OpenAI publishes (that 1.5%→2.0%) don't even measure this case —they're how often the model refuses on offensive tasks, not how well it reviews your code—, so I don't lean on them, I lean on my ten runs. For the dev who wants to secure the app they built over the weekend, the news is calm: the tool you already have is enough. Run it more than once, read what it tells you with suspicion, and fix it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The ten raw reports (five per program) and the prompt are in &lt;a href="https://gist.github.com/JuanTorchia/6248910b6e7c2235f286c40e8de57c95" rel="noopener noreferrer"&gt;this gist&lt;/a&gt;. Run the &lt;code&gt;standard&lt;/code&gt; side on your own app and tell me if you get something different.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>english</category>
      <category>llm</category>
      <category>seguridad</category>
      <category>openai</category>
    </item>
    <item>
      <title>Corrí el audit de seguridad de OpenAI 10 veces: para revisar tu código, el ChatGPT normal alcanza</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:36:13 +0000</pubDate>
      <link>https://dev.to/jtorchia/corri-el-audit-de-seguridad-de-openai-10-veces-para-revisar-tu-codigo-el-chatgpt-normal-alcanza-1e5d</link>
      <guid>https://dev.to/jtorchia/corri-el-audit-de-seguridad-de-openai-10-veces-para-revisar-tu-codigo-el-chatgpt-normal-alcanza-1e5d</guid>
      <description>&lt;p&gt;Hay un patrón que se repite en el timeline: alguien vibecodea una app en un finde, la sube, le va bien, y a la semana queda con la paranoia de "¿esto es seguro?". Y justo ahí aparece el pitch de OpenAI: Daybreak Blue, el carril defensivo de su stack de ciberseguridad, "para revisión de código seguro, descubrimiento de vulnerabilidades y validación de parches". O sea, exactamente lo que ese dev necesita.&lt;/p&gt;

&lt;p&gt;Tengo acceso a Blue. Así que en vez de creerle al anuncio corrí el experimento: agarré una app real, le pasé el &lt;strong&gt;mismo pedido, palabra por palabra&lt;/strong&gt;, al ChatGPT estándar y a Blue, y comparé. La pregunta que le contesta esto a cualquiera que vibecodea es simple: para asegurar tu app, ¿el tier especial hace algo que no hace el ChatGPT de siempre?&lt;/p&gt;

&lt;p&gt;Lo corrí una vez y me dio una respuesta limpia y compartible. Casi la publico. Menos mal que la corrí nueve veces más.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué es "Blue", en criollo
&lt;/h2&gt;

&lt;p&gt;Daybreak &lt;strong&gt;Blue&lt;/strong&gt; no es otro modelo: corre sobre el mismo modelo base (Sol) con las salvaguardas de ciberseguridad aflojadas para trabajo autorizado. El que sí trae un modelo aparte es el otro tier, Red —GPT-5.6-Cyber—, que no está disponible para individuos. En el Codex que me habilitaron, el programa se elige con un flag, &lt;code&gt;--cyber-access-program&lt;/code&gt;, que pongo en &lt;code&gt;standard&lt;/code&gt; (el ChatGPT de siempre) o &lt;code&gt;daybreak_blue&lt;/code&gt;; no figura en la documentación pública de OpenAI, lo sé porque es el que corro. Así audité &lt;strong&gt;el mismo modelo base&lt;/strong&gt; cambiando &lt;strong&gt;solo&lt;/strong&gt; esa perilla. Esa es la única variable —al menos la única que OpenAI dice que cambia: desde afuera no puedo auditar que Blue no toque también el system prompt o el scaffold, así que lo tomo como lo que es, la palabra del vendor—.&lt;/p&gt;

&lt;p&gt;Y ojo, que esto cambia qué podés reproducir vos: a Blue no entrás tildando una casilla. Hay que tener un plan pago con Advanced Account Security, registrar una llave física FIDO2 —sí, una YubiKey; de eso fue el primer post de esta serie—, verificar identidad con selfie y documento en &lt;code&gt;chatgpt.com/cyber&lt;/code&gt;, y justificar para qué lo vas a usar. OpenAI revisa caso por caso y puede rechazarte: completar el setup no garantiza el acceso (&lt;a href="https://help.openai.com/en/articles/20001258-openai-daybreak-trusted-access-for-cyber-overview" rel="noopener noreferrer"&gt;el proceso oficial, acá&lt;/a&gt;). Por eso, de este experimento vos podés reproducir el lado &lt;code&gt;standard&lt;/code&gt; —ese es el ChatGPT que ya tenés— pero no necesariamente el &lt;code&gt;daybreak_blue&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El setup (reproducible)
&lt;/h2&gt;

&lt;p&gt;Target: &lt;a href="https://github.com/anchorcms/anchor-cms" rel="noopener noreferrer"&gt;anchor-cms&lt;/a&gt;, un CMS en PHP, 3.300 estrellas, en uso real pero abandonado desde 2018. Router, auth y capa de base de datos hechos a mano —el lugar donde hay bugs de verdad, y también el caso más fácil posible para cualquier modelo: PHP viejo, sin framework que tape nada—.&lt;/p&gt;

&lt;p&gt;Mismo prompt para los dos, sandbox de solo lectura (lee el código, sin red, sin ejecutar nada), misma máquina, mismo commit. Este es el prompt exacto, por si querés correrlo en tu propia app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sos un analista de seguridad haciendo revisión de código defensiva sobre
este repositorio. Trabajá solo leyendo el código; no accedas a la red ni
ejecutes la app.
1) Threat modeling: qué entradas no confiables procesa y por dónde entran.
2) Vulnerabilidades concretas: tipo, archivo:línea, por qué es explotable
   (cadena desde la entrada hasta el sink), severidad.
3) Rankealas por evidencia e impacto.
4) Para las más graves, proponé el parche.
Sé honesto: si algo parece vulnerable pero está mitigado, decilo. No
inventes hallazgos para llenar la lista. Citá siempre archivo:línea.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Le pido "no inventes" igual a los dos, pero no me engaño: pedirle honestidad a un modelo no garantiza nada. Lo que filtra el relleno es que yo verifique cada hallazgo contra el código, no la buena voluntad del prompt.)&lt;/p&gt;

&lt;p&gt;¿Y cómo le das tu repo? Lo más simple: subí los archivos a un Proyecto de ChatGPT o Claude, o usá una herramienta de agente (Codex, Cursor, Claude Code) que ya ve tu código, y pegá el prompt de arriba.&lt;/p&gt;

&lt;h2&gt;
  
  
  Primera sorpresa: ninguno se negó
&lt;/h2&gt;

&lt;p&gt;Buena parte del relato de Daybreak se apoya en una idea: el modelo normal tiene frenos para seguridad, Blue te los saca. OpenAI hasta publica el número. Y acá está el detalle que casi nadie lee: ese número —el modelo general completa el &lt;strong&gt;1,5%&lt;/strong&gt; de los pedidos, Blue el &lt;strong&gt;2,0%&lt;/strong&gt;— es sobre tareas &lt;strong&gt;ofensivas&lt;/strong&gt;: desarrollo de cadenas de exploit, bypass de autenticación, escalada de privilegios &lt;em&gt;para atacar&lt;/em&gt; (&lt;a href="https://www.infosecurity-magazine.com/news/openai-daybreak-blue-red-gpt-cyber/" rel="noopener noreferrer"&gt;Infosecurity Magazine&lt;/a&gt;). El salto grande (95%) es de otro modelo, GPT-5.6-Cyber, que solo da el tier Red.&lt;/p&gt;

&lt;p&gt;Revisar &lt;strong&gt;tu propio código&lt;/strong&gt; para protegerlo no es ninguna de esas cosas. Y se notó: las diez corridas, estándar y Blue, hicieron la auditoría completa sin un solo "no puedo ayudarte con esto". El freno nunca se activó porque mi tarea jamás estuvo en la categoría que el freno bloquea. Eso solo ya contesta medio post: para asegurar tu app, el ChatGPT que tenés no se planta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué encontraron los dos, siempre
&lt;/h2&gt;

&lt;p&gt;Lo verifiqué leyendo el código, no de palabra —cadenas plausibles según el código, no probadas en ejecución: nunca corrí un exploit—. En las diez corridas aparecieron, sin fallar, los tres bugs graves:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escalada de privilegios.&lt;/strong&gt; Un usuario de bajo privilegio se vuelve administrador. En la ruta de editar usuario hay, literal, esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Force admin for now&lt;/span&gt;
&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'administrator'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El filtro de autorización solo chequea que haya sesión, nunca el rol. Editás tu propio perfil y salís admin. Con una salvedad que los reportes marcan y que vale decir: la instalación default de anchor-cms fuerza &lt;code&gt;administrator&lt;/code&gt; también al &lt;strong&gt;crear&lt;/strong&gt; usuarios, así que de fábrica no hay cuentas de bajo privilegio desde donde escalar. El bug muerde en instalaciones migradas, con datos importados o seed manual, donde esas cuentas sí existen. Catalogado como &lt;a href="https://app.opencve.io/cve/CVE-2026-88959" rel="noopener noreferrer"&gt;CVE-2026-88959&lt;/a&gt; (CVSS 8.7 según el registro), publicado un mes antes de este experimento —después del corte de entrenamiento de los dos modelos, así que lo encontraron leyendo, no recordando—.&lt;/p&gt;

&lt;p&gt;Los otros dos constantes: &lt;strong&gt;toma de cuenta por el header &lt;code&gt;Host&lt;/code&gt;&lt;/strong&gt; en el reset de contraseña (el link se arma con &lt;code&gt;$_SERVER['HTTP_HOST']&lt;/code&gt; sin validar) y &lt;strong&gt;CSRF por GET&lt;/strong&gt; (la protección solo valida &lt;code&gt;POST&lt;/code&gt;, y todos los borrados son &lt;code&gt;GET&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Hasta acá, lo que me dio la primera corrida. Su conclusión: "Blue encontró un cuarto crítico que el estándar no vio (el instalador), el estándar encontró dos de sesión que Blue no. Casi empate, leve ventaja de Blue en profundidad." Lindo, equilibrado, falso.&lt;/p&gt;

&lt;h2&gt;
  
  
  El giro: lo corrí diez veces
&lt;/h2&gt;

&lt;p&gt;El LLM no es determinista. Le pedís lo mismo dos veces y te contesta distinto. Así que antes de publicar "Blue encontró X y el estándar no", lo corrí &lt;strong&gt;cinco veces por lado&lt;/strong&gt; y conté en cuántas aparecía cada hallazgo. Son dos ejes distintos: qué tan grave es el bug (verificado contra el código) y en cuántas corridas lo agarró cada lado. La detección es ruidosa; la severidad no cambia con ella.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hallazgo&lt;/th&gt;
&lt;th&gt;Severidad&lt;/th&gt;
&lt;th&gt;Detección estándar&lt;/th&gt;
&lt;th&gt;Detección Blue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Privilege escalation (CVE, condicionado)&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSRF por GET&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host-header en el reset&lt;/td&gt;
&lt;td&gt;Media-alta&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instalador abierto → takeover&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;td&gt;2/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sesiones / enumeración / fuga de errores&lt;/td&gt;
&lt;td&gt;Media-baja&lt;/td&gt;
&lt;td&gt;ruido, ambos&lt;/td&gt;
&lt;td&gt;ruido, ambos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XSS reflejado (borderline, discutido)&lt;/td&gt;
&lt;td&gt;Baja/dudosa&lt;/td&gt;
&lt;td&gt;0/5&lt;/td&gt;
&lt;td&gt;3/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LFI por &lt;code&gt;pagetype&lt;/code&gt; (condicional)&lt;/td&gt;
&lt;td&gt;Baja/condicional&lt;/td&gt;
&lt;td&gt;0/5&lt;/td&gt;
&lt;td&gt;2/5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El "crítico del instalador" que yo le había colgado a Blue como hallazgo exclusivo &lt;strong&gt;lo encuentra el doble de seguido el estándar&lt;/strong&gt;: 4 de 5 corridas contra 2 de 5 de Blue. Mi comparación lo tenía al revés por pura casualidad de muestreo. Si publicaba la primera corrida, publicaba una casualidad como si fuera un hallazgo.&lt;/p&gt;

&lt;p&gt;La enumeración, la fijación de sesión y la fuga de trazas aparecen en los dos modelos de forma pareja: están, pero no distinguen a uno del otro. Los únicos dos hallazgos que salen el 100% de las veces en los dos lados son el privesc y el CSRF; el host-header queda en un empate estable, 4 de 5 en cada uno. Donde hay consistencia, hay empate.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Blue encontró algo que el estándar no? No exactamente
&lt;/h2&gt;

&lt;p&gt;Lo que a primera vista parece la ventaja de Blue: reportó dos cosas que el estándar nunca listó como hallazgo —un XSS reflejado por valores "flasheados" del formulario y una inclusión de archivo local vía el campo &lt;code&gt;pagetype&lt;/code&gt;, que se concatena a una ruta sin sanitizar—. Suena a que Blue vio algo que al estándar se le escapó. Pero cuando leés los reportes crudos, al menos en el caso más fuerte —el XSS— no es eso lo que pasó: el estándar &lt;strong&gt;miró el mismo código y lo descartó con argumento&lt;/strong&gt;. Una corrida lo rastrea hasta el mecanismo interno &lt;code&gt;Input::flash()&lt;/code&gt; y concluye, textual, que "no afirma una cadena XSS explotable con el código actual". Con el &lt;code&gt;pagetype&lt;/code&gt; es distinto y hay que ser honesto: el estándar no lo trazó ni lo descartó, directamente no lo levantó como vulnerabilidad en ninguna de sus cinco corridas. Ahí sí puede haber un sink que no llegó a mirar.&lt;/p&gt;

&lt;p&gt;O sea: en el XSS no es un gap de cobertura (Blue encontró, el estándar no), es un &lt;strong&gt;desacuerdo de criterio&lt;/strong&gt; —Blue lo reporta como hallazgo; el estándar lo analiza y lo juzga no explotable—. En el &lt;code&gt;pagetype&lt;/code&gt; el reparto es más gris, puede que el estándar no haya llegado a ese sink. Pero en ninguno de los dos casos estamos ante un bug que te hunda: el XSS es tan borderline que el propio Blue no lo reporta en 2 de cada 5 corridas, y el &lt;code&gt;pagetype&lt;/code&gt; es un LFI condicional de baja severidad. Si el criterio del estándar es el correcto, los reportes de Blue son falsos positivos; y aun si no, más &lt;em&gt;amplitud&lt;/em&gt; en lo borderline no es obviamente mejor: para un auditor, un falso positivo bien descartado vale tanto como un hallazgo.&lt;/p&gt;

&lt;p&gt;Y la estadística remata lo mismo. Con cinco corridas por lado, un 3/5 contra 0/5 tiene ~17% de chances de salir así de puro azar aunque los dos tiers fueran idénticos (test de Fisher): ni cerca de significativo. Este diseño solo podría probar una diferencia casi total —4 o 5 de 5 contra 0—, y lo único que llega a esa consistencia son los bugs graves, que &lt;strong&gt;empatan&lt;/strong&gt; (y encima, mirando trece hallazgos distintos, que al menos uno salga tan torcido es puro azar, no señal). Aclaración de método: conté un hallazgo solo si el reporte lo lista como vulnerabilidad accionable, no si lo menciona para descartarlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El veredicto, con los números en la mano
&lt;/h2&gt;

&lt;p&gt;Para la pregunta concreta —"¿me conviene el tier especial para asegurar mi app?"— la respuesta es &lt;strong&gt;no, casi no cambia nada&lt;/strong&gt;, y ahora lo puedo decir con datos y no con una corrida afortunada. Los dos modelos encontraron los bugs que te hunden en las diez corridas, sin fallar una sola vez. El tier especializado no agregó ni un hallazgo grave que el estándar se perdiera: lo que sumó fue ruido en lo borderline. Nada más.&lt;/p&gt;

&lt;p&gt;Un límite honesto antes de que generalices: esto es &lt;strong&gt;una&lt;/strong&gt; app y &lt;strong&gt;un&lt;/strong&gt; prompt. Las cinco corridas me matan el azar &lt;em&gt;dentro&lt;/em&gt; de ese caso, no te prometen lo mismo en tu microservicio Go o en tu app sobre un framework que ya te tapa medio OWASP. anchor-cms es PHP viejo, hecho a mano, sin red de contención: el caso más favorable para que &lt;em&gt;cualquier&lt;/em&gt; modelo brille. Leé la conclusión como "para este tipo de target", no como ley universal.&lt;/p&gt;

&lt;p&gt;Y un límite más, esta vez del lado de Blue: elegí a propósito la tarea defensiva más inofensiva —leer código, sin ejecutar ni escribir un exploit—, que es justo donde el freno del modelo estándar nunca se iba a activar. Blue también se vende para validar parches con prueba de concepto, analizar malware o responder incidentes; varias de esas sí generan artefactos ofensivos y sí le disparan negativas al estándar. Ahí Blue podría destrabar lo que el ChatGPT normal te rebota. No lo testeé: mi experimento mide el sub-caso donde, por diseño, Blue no tenía cómo ganar.&lt;/p&gt;

&lt;p&gt;Las lecciones, que no son sobre qué modelo usás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Un modelo capaz encuentra los bugs graves de tu app de forma confiable.&lt;/strong&gt; Buenísimo… y también quiere decir que cualquiera con la misma herramienta los encuentra igual de rápido. Si vibecodeás, asumí que tu código va a ser auditado por un modelo: hacelo vos primero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lo corras una sola vez.&lt;/strong&gt; Esta es la parte que casi me come a mí. Una corrida es una muestra de una variable aleatoria. Corré el mismo prompt tres o cuatro veces y quedate con la unión de los hallazgos; la mitad de las cosas útiles aparecen recién en la segunda o la tercera.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verificá.&lt;/strong&gt; El bug más grave era un comentario &lt;code&gt;// Force admin for now&lt;/code&gt; a la vista, y aun así cada corrida se perdía cosas distintas. El modelo te acelera; no te reemplaza el criterio. Pedile archivo y línea, pedile que descarte falsos positivos, y seguí la cadena vos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nada de esto es contra Daybreak. Pero ojo con el argumento: los números que publica OpenAI (ese 1,5%→2,0%) ni siquiera miden este caso —son cuánto se niega el modelo en tareas ofensivas, no qué tan bien revisa tu código—, así que no me apoyo en ellos, me apoyo en mis diez corridas. Para el dev que quiere asegurar la app que armó el finde, la noticia es tranquila: la herramienta que ya tenés alcanza. Corréla más de una vez, leé lo que te dice con desconfianza, y arreglá.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Los diez reportes crudos (cinco por programa) y el prompt están en &lt;a href="https://gist.github.com/JuanTorchia/6248910b6e7c2235f286c40e8de57c95" rel="noopener noreferrer"&gt;este gist&lt;/a&gt;. Corré el lado &lt;code&gt;standard&lt;/code&gt; en tu propia app y contame si te da distinto.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>llm</category>
      <category>seguridad</category>
    </item>
    <item>
      <title>gRPC shows up when REST starts breaking under its own weight</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Sat, 10 Oct 2026 12:00:20 +0000</pubDate>
      <link>https://dev.to/jtorchia/grpc-shows-up-when-rest-starts-breaking-under-its-own-weight-4oab</link>
      <guid>https://dev.to/jtorchia/grpc-shows-up-when-rest-starts-breaking-under-its-own-weight-4oab</guid>
      <description>&lt;p&gt;Picture this: you've got fifteen microservices talking to each other. Each one exposes its own REST API, each one has its own JSON with its own naming conventions, and every time someone changes a field in the payments service, three other teams find out when everything blows up in production. You document with Swagger, but the Swagger goes stale. You write HTTP clients by hand, or generate something with OpenAPI that works "more or less." And when you need service A to ask service B something ten thousand times a second, the overhead of JSON + HTTP/1.1 starts hurting your infrastructure bill.&lt;/p&gt;

&lt;p&gt;That specific pain — inter-service communication with contracts that break silently and performance that doesn't scale — is exactly what gRPC comes to solve. It's not a trend. Google designed it to solve that exact problem inside its own infrastructure, and then released it as an open source project. Today it shows up in three independent awesome lists, which, in the curation system I've been building out through this series, is a strong signal: it's not noise, it's real community consensus.&lt;/p&gt;

&lt;p&gt;If you've been working with REST your whole career (like I spent a good chunk of time managing infrastructure before diving fully into development), the first time you see gRPC in action it changes how you think. It's not "another HTTP framework." It's a different paradigm for how your services talk to each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/grpc/grpc" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; is a Remote Procedure Call framework that runs over HTTP/2 and uses Protocol Buffers (protobuf) as its default serialization mechanism. Instead of exposing REST endpoints and letting every client build its own request, you define a contract — a &lt;code&gt;.proto&lt;/code&gt; file — and from that you generate client and server code in whatever language you need. Go, Java, Python, C++, Node, whatever. Same contract, multiple languages, and the field names and types are enforced by the generated code instead of living only in a doc someone forgot to update.&lt;/p&gt;

&lt;p&gt;Here's what a basic contract looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pedidos.proto&lt;/span&gt;
&lt;span class="c1"&gt;// We define the service and the messages it will exchange&lt;/span&gt;
&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;pedidos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The service exposes an RPC method, as if it were a remote function&lt;/span&gt;
&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;PedidosService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ObtenerPedido&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PedidoRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PedidoResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Messages are like DTOs, but typed and versionable&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PedidoRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id_pedido&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id_pedido&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="kt"&gt;double&lt;/span&gt; &lt;span class="na"&gt;monto_total&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="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;estado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;From that file, the protobuf compiler (&lt;code&gt;protoc&lt;/code&gt;) automatically generates serialization classes and client/server stubs. If you come from the Java world (like me, where I started my career as a developer), this is going to sound similar to what SOAP did with WSDL, but without the dead weight of XML and with noticeably better performance.&lt;/p&gt;

&lt;p&gt;On the server side in Java, the implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We implement the service automatically generated from the .proto&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PedidosServiceImpl&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PedidosServiceGrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PedidosServiceImplBase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;obtenerPedido&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PedidoRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                               &lt;span class="nc"&gt;StreamObserver&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PedidoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The real logic would go here, querying the database&lt;/span&gt;
        &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIdPedido&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIdPedido&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMontoTotal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1500.50&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEstado&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CONFIRMADO"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// onNext sends the response, onCompleted closes the stream&lt;/span&gt;
        &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onCompleted&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there's no JSON anywhere. The payload is serialized in binary, which means smaller payloads and faster parsing. And since it runs over HTTP/2, you get multiplexing of requests over a single TCP connection, native bidirectional streaming, and header compression — the structural reasons gRPC tends to outperform REST under high load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's on the list
&lt;/h2&gt;

&lt;p&gt;The consensus signal — three independent awesome lists mentioning it — isn't a coincidence. gRPC solves a problem that comes up fast with microservices: the lack of strong contracts between services. With REST + JSON, the contract is a convention that lives in devs' heads and in documentation that goes stale. With gRPC, the contract is generated code. If you break a field, you find out at compile time, not in production on a Friday at six in the evening.&lt;/p&gt;

&lt;p&gt;The other big reason is performance. Code generation eliminates manual serialization boilerplate — you don't write JSON parsers by hand, you don't maintain duplicate DTOs in every language. And binary protobuf over HTTP/2 is the combination that gives gRPC an edge over traditional REST under high load, which is exactly the scenario where microservices start to struggle.&lt;/p&gt;

&lt;p&gt;It's the kind of tool that doesn't shine in a five-minute demo, but becomes indispensable when you have dozens of services talking to each other at real volume. That's where the JVM ecosystem (if you work with Spring Boot, there's solid support via grpc-spring-boot-starter) benefits from this approach versus maintaining fifteen hand-built REST clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to use it
&lt;/h2&gt;

&lt;p&gt;Now, let's be honest: gRPC isn't free, and this is the part people skip. Protobuf and the async/streaming model are a different mental model from request/response REST — if your team hasn't touched this before, budget real ramp-up time for it, not an afternoon. Debugging is also more tedious: you can't just hit a gRPC endpoint with curl like you did with REST, you need extra tools like &lt;a href="https://github.com/fullstorydev/grpcurl" rel="noopener noreferrer"&gt;grpcurl&lt;/a&gt; or &lt;a href="https://github.com/bloomrpc/bloomrpc" rel="noopener noreferrer"&gt;BloomRPC&lt;/a&gt; to inspect traffic.&lt;/p&gt;

&lt;p&gt;It also doesn't make sense if you're going to consume your API directly from a browser (you need gRPC-Web with an intermediate proxy, another layer of complexity) or if you're building a public API that third parties will consume without any control over their stack — there, REST + OpenAPI is still more friendly. If your system is two or three services with low traffic, you're probably adding complexity you don't need. For those cases, stick with REST or look at lighter alternatives like &lt;a href="https://github.com/trpc/trpc" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt; if you're in a TypeScript monorepo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;gRPC is the kind of tool you shouldn't adopt because it's trendy, but because you have the specific problem it solves: inter-service communication at scale, with strong contracts and performance that matters. That's why it made it through this series' filters — it's not hype, it's real technical consensus, and it comes with a real learning cost you should plan for, not skip.&lt;/p&gt;

&lt;p&gt;If you liked this analysis, this is just entry #13 of &lt;strong&gt;Awesome Curated: The Tools&lt;/strong&gt;, where I go through tools that passed our curation system's filter. If you're into infrastructure and runtimes, you might also like the post on &lt;a href="https://juanchi.dev/en/blog/nodejs-runtime-that-changed-backend-forever" rel="noopener noreferrer"&gt;Node.js and its event loop&lt;/a&gt;, or if you're into networking, &lt;a href="https://juanchi.dev/en/blog/sniffnet-monitor-network-traffic-without-tcpdump" rel="noopener noreferrer"&gt;Sniffnet for monitoring network traffic without losing your mind&lt;/a&gt;. The full series is at &lt;a href="https://juanchi.dev/en/blog/series/awesome-curated-tools" rel="noopener noreferrer"&gt;/blog/series/awesome-curated-tools&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://juanchi.dev/en/blog/grpc-when-rest-isnt-enough-microservices" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>english</category>
      <category>java</category>
      <category>rpc</category>
      <category>http2</category>
    </item>
    <item>
      <title>gRPC no es moda: es el contrato que REST nunca te dio</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Sat, 10 Oct 2026 12:00:16 +0000</pubDate>
      <link>https://dev.to/jtorchia/grpc-no-es-moda-es-el-contrato-que-rest-nunca-te-dio-549j</link>
      <guid>https://dev.to/jtorchia/grpc-no-es-moda-es-el-contrato-que-rest-nunca-te-dio-549j</guid>
      <description>&lt;p&gt;Imaginate esto: tenés quince microservicios hablando entre sí. Cada uno expone su API REST, cada uno tiene su propio JSON con sus propias convenciones de nombres, y cada vez que alguien cambia un campo en el servicio de pagos, tres equipos más se enteran cuando todo explota en producción. Documentás con Swagger, pero el Swagger se desactualiza. Escribís clientes HTTP a mano, o generás algo con OpenAPI que funciona "más o menos". Y cuando necesitás que el servicio A le pregunte algo al servicio B diez mil veces por segundo, el overhead de JSON + HTTP/1.1 empieza a dolerte en la factura de infraestructura.&lt;/p&gt;

&lt;p&gt;Ese dolor específico — comunicación inter-servicios con contratos que se rompen en silencio y performance que no escala — es el que gRPC viene a resolver. No es una moda. Google lo diseñó para resolver ese problema puntual dentro de su propia infraestructura, y después lo liberó como proyecto open source. Hoy aparece en tres awesome lists independientes, lo cual en el sistema de curación que vengo laburando en esta serie es señal fuerte: no es ruido, es consenso real de la comunidad.&lt;/p&gt;

&lt;p&gt;Si vos venís de laburar con REST toda la vida (como yo laburé un buen tiempo administrando infraestructura antes de meterme de lleno al desarrollo), la primera vez que ves gRPC en acción te cambia la cabeza. No es "otro framework HTTP". Es un paradigma distinto para pensar cómo se hablan tus servicios.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/grpc/grpc" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; es un framework de Remote Procedure Calls que corre sobre HTTP/2 y usa Protocol Buffers (protobuf) como mecanismo de serialización por default. En vez de exponer endpoints REST y que cada cliente arme su propio request, definís un contrato — un archivo &lt;code&gt;.proto&lt;/code&gt; — y de ahí se genera código cliente y servidor en el lenguaje que necesites: Go, Java, Python, C++, Node. El mismo contrato, múltiples lenguajes, menos espacio para que cada equipo interprete el JSON a su manera.&lt;/p&gt;

&lt;p&gt;Mirá cómo se ve un contrato básico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pedidos.proto&lt;/span&gt;
&lt;span class="c1"&gt;// Definimos el servicio y los mensajes que va a intercambiar&lt;/span&gt;
&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;pedidos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// El servicio expone un método RPC, como si fuera una función remota&lt;/span&gt;
&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;PedidosService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;ObtenerPedido&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PedidoRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PedidoResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Los mensajes son como DTOs, pero tipados y versionables&lt;/span&gt;
&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PedidoRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id_pedido&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id_pedido&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="kt"&gt;double&lt;/span&gt; &lt;span class="na"&gt;monto_total&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="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;estado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;De ese archivo, el compilador de protobuf (&lt;code&gt;protoc&lt;/code&gt;) genera automáticamente las clases de serialización y los stubs de cliente/servidor. Si vos venís del mundo Java (como yo, que arranqué ahí mi carrera como developer), esto te va a sonar parecido a lo que hacía SOAP con WSDL, pero sin el peso muerto de XML.&lt;/p&gt;

&lt;p&gt;Del lado del servidor en Java, la implementación queda así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implementamos el servicio generado automáticamente desde el .proto&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PedidosServiceImpl&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PedidosServiceGrpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PedidosServiceImplBase&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;obtenerPedido&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PedidoRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                               &lt;span class="nc"&gt;StreamObserver&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PedidoResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Acá iría la lógica real, consultando la base de datos&lt;/span&gt;
        &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PedidoResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIdPedido&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIdPedido&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMontoTotal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1500.50&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEstado&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CONFIRMADO"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// onNext envía la respuesta, onCompleted cierra el stream&lt;/span&gt;
        &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;responseObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onCompleted&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fijate que no hay JSON por ningún lado. El payload va serializado en binario, lo cual en teoría significa payloads más chicos y parsing más rápido que un JSON equivalente. Y como corre sobre HTTP/2, tenés multiplexación de requests sobre una sola conexión TCP, streaming bidireccional nativo y header compression — las piezas que, en el paper y la documentación oficial de gRPC, explican la ventaja frente a REST + HTTP/1.1 en escenarios de tráfico alto. No tengo benchmarks propios corridos para este post; lo dejo como lo que es, una ventaja de diseño documentada por el proyecto, no una medición mía.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué está en la lista
&lt;/h2&gt;

&lt;p&gt;La señal de consenso — tres awesome lists independientes mencionándolo — no es casualidad. gRPC apunta a un problema que cualquiera que laburó con microservicios a escala conoce: la falta de contratos fuertes entre servicios. Con REST + JSON, el contrato es una convención que vive en la cabeza de los devs y en una documentación que se desactualiza. Con gRPC, el contrato es código generado: si cambiaste un campo y rompiste algo, el compilador te lo marca, no lo descubrís en producción un viernes a las seis de la tarde.&lt;/p&gt;

&lt;p&gt;La otra razón es que el code generation elimina el boilerplate de serialización manual — no escribís parsers de JSON a mano, no mantenés DTOs duplicados en cada lenguaje. Eso es valor concreto, independiente de cuánto gane o no en performance tu caso puntual.&lt;/p&gt;

&lt;p&gt;Es el tipo de herramienta que no brilla en un demo de cinco minutos, pero que tiene sentido cuando tenés una arquitectura con decenas de servicios hablando entre sí. Ahí es donde el ecosistema JVM (si laburás con Spring Boot, hay soporte vía grpc-spring-boot-starter) se beneficia de este approach frente a mantener quince clientes REST hechos a mano.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuándo NO usarlo
&lt;/h2&gt;

&lt;p&gt;Ahora, seamos honestos: gRPC no es gratis, y esto es lo que más me interesa dejar claro. Protobuf y los patrones async/streaming tienen una curva de entrada que no es trivial — si tu equipo nunca laburó con esto, contá con perder tiempo entendiendo por qué el stub generado no hace lo que esperabas en el primer intento. No tengo un número de días para darte porque depende del equipo, pero es una friccion real, no un detalle menor.&lt;/p&gt;

&lt;p&gt;El debugging distribuido también es más tedioso: no podés pegarle con curl a un endpoint gRPC como hacías con REST, necesitás herramientas extra como &lt;a href="https://github.com/fullstorydev/grpcurl" rel="noopener noreferrer"&gt;grpcurl&lt;/a&gt; o &lt;a href="https://github.com/bloomrpc/bloomrpc" rel="noopener noreferrer"&gt;BloomRPC&lt;/a&gt; para inspeccionar tráfico.&lt;/p&gt;

&lt;p&gt;Tampoco tiene sentido si tu API la vas a consumir directamente desde un browser (necesitás gRPC-Web con un proxy intermedio, otra capa de complejidad) o si estás armando una API pública que terceros van a consumir sin control sobre su stack — ahí REST + OpenAPI sigue siendo más amigable. Si tu sistema son dos o tres servicios con tráfico bajo, probablemente estés agregando complejidad que no necesitás. Para esos casos, quedate con REST o mirá alternativas más livianas como &lt;a href="https://github.com/trpc/trpc" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt; si estás en un monorepo TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;gRPC tiene sentido cuando tenés el problema específico que resuelve: comunicación inter-servicios a escala, con contratos fuertes y una curva de aprendizaje que estás dispuesto a pagar. No lo adoptaría solo porque aparece en tres awesome lists — esa señal te dice que la comunidad lo validó, no que tu equipo esté listo para el costo de entrada.&lt;/p&gt;

&lt;p&gt;Esto es la entrega #13 de &lt;strong&gt;Awesome Curated: The Tools&lt;/strong&gt;, donde voy desmenuzando herramientas que pasaron el filtro de nuestro sistema de curación. Si te interesa el lado de infraestructura y runtimes, capaz te cope también el post sobre &lt;a href="https://juanchi.dev/es/blog/nodejs-runtime-javascript-backend-event-loop-ecosystem" rel="noopener noreferrer"&gt;Node.js y su event loop&lt;/a&gt;, o si andás por el lado de redes, &lt;a href="https://juanchi.dev/es/blog/sniffnet-monitor-trafico-red-ui-accesible-rust" rel="noopener noreferrer"&gt;Sniffnet para monitorear tráfico sin volverte loco&lt;/a&gt;. La serie completa está en &lt;a href="https://juanchi.dev/es/blog/series/awesome-curated-tools" rel="noopener noreferrer"&gt;/blog/series/awesome-curated-tools&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Este artículo fue publicado originalmente en &lt;a href="https://juanchi.dev/es/blog/grpc-rpc-http2-microservicios-protobuf" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>java</category>
      <category>rpc</category>
    </item>
    <item>
      <title>Apache Spark won't melt down when your data doesn't fit in RAM anymore</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:00:23 +0000</pubDate>
      <link>https://dev.to/jtorchia/apache-spark-wont-melt-down-when-your-data-doesnt-fit-in-ram-anymore-1gic</link>
      <guid>https://dev.to/jtorchia/apache-spark-wont-melt-down-when-your-data-doesnt-fit-in-ram-anymore-1gic</guid>
      <description>&lt;p&gt;Hey, before I start I need to make an honest clarification, because in this series transparency is sacred: the automated analysis I was handed for this post talked about a "memory layer for AI agents," vector DBs, and cross-session context persistence. That has nothing to do with what Apache Spark actually is. That description belongs to a different tool — probably something like Mem0 or similar — and it got crossed wrong in the curation pipeline. It happens, automated systems sometimes mix up metadata. That's why the human verdict still says "pending" — here it is, done by hand, with the real data.&lt;/p&gt;

&lt;p&gt;What I do have confirmed: Apache Spark shows up in &lt;strong&gt;3 independent awesome lists&lt;/strong&gt;, and the official repo description describes it as a framework with "micro-batch processing for streams" and "stateful exactly-once semantics" as a backend. In plain terms: it's a distributed engine built for workloads where a single server, no matter how much RAM you throw at it, becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;Picture this: you have logs from an app generating 500 GB per day, and you need to calculate aggregate metrics — unique users, average latencies, anomaly detection — without waiting 14 hours for a Python script running on a single core to finish. That's where Spark stops being an academic curiosity and becomes the tool that saves your sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Apache Spark is a distributed data processing engine, originally written in Scala, that runs on the JVM (yes, the JVM again — there's a reason I keep mentioning it in this series). It was born in Berkeley's AMPLab as a direct response to Hadoop MapReduce's limitations: Spark processes in memory instead of writing to disk at every intermediate step, which makes it orders of magnitude faster for iterative workloads (think ML algorithms, which need to pass over the same data again and again).&lt;/p&gt;

&lt;p&gt;The central concept is the &lt;strong&gt;RDD&lt;/strong&gt; (Resilient Distributed Dataset): a collection of data partitioned across the cluster's nodes, immutable, and able to rebuild itself if a node goes down (hence "resilient"). On top of that sit friendlier layers like DataFrames and Datasets, which give you a SQL/Pandas-style API, but distributed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Classic example: counting words in a giant dataset&lt;/span&gt;
&lt;span class="c1"&gt;// distributed across all nodes in the cluster&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.spark.sql.SparkSession&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;spark&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;SparkSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;appName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ContadorDePalabras"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;getOrCreate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;textFile&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;spark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;read&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;textFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hdfs://logs/*.txt"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;conteo&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;textFile&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;linea&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;// split into words&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;groupByKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                  &lt;span class="c1"&gt;// group by word&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;count&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;                               &lt;span class="c1"&gt;// count occurrences&lt;/span&gt;

&lt;span class="nv"&gt;conteo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;show&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nv"&gt;spark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is that this exact same code runs the same way on your notebook with 4 GB of RAM as it does on a 200-node cluster on AWS EMR. Spark takes care of partitioning the data, distributing the tasks, and handling failures — you just describe &lt;em&gt;what&lt;/em&gt; you want to do, not &lt;em&gt;how&lt;/em&gt; to distribute it.&lt;/p&gt;

&lt;p&gt;The streaming part (the one the official repo description mentioned, "micro-batch processing for streams... stateful exactly-once semantics") is Spark Structured Streaming: instead of processing event by event like Kafka Streams or Flink, Spark groups events into micro-batches (every few seconds) and processes them with the same API you use for batch. That gives you "exactly-once" — no event gets processed twice or lost, even if a node explodes at the worst possible moment.&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="c1"&gt;# Streaming in PySpark: counting events by time window
# reading from a Kafka topic in real time
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readStream&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kafka&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kafka.bootstrap.servers&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;localhost:9092&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subscribe&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;eventos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;conteoPorVentana&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1 minute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conteoPorVentana&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeStream&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;awaitTermination&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is open source under the Apache 2.0 license, maintained by the Apache Software Foundation, with official support for Scala, Java, Python (PySpark), and R. The repo is at &lt;a href="https://github.com/apache/spark" rel="noopener noreferrer"&gt;github.com/apache/spark&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's on the list
&lt;/h2&gt;

&lt;p&gt;Showing up in 3 independent awesome lists is a decent signal — different curators, different angles, pointing at the same tool. It doesn't prove consensus across entire industries, but it's more than "trending on Twitter": it suggests the tool holds up across more than one context.&lt;/p&gt;

&lt;p&gt;What sets it apart from alternatives like Hadoop MapReduce (its direct predecessor, much slower because it writes to disk at every step) or Dask (more pythonic but with an ecosystem and maturity well below) is the combination of three things: in-memory speed, a unified API that serves both batch and streaming, and an ecosystem of integrated libraries (MLlib for distributed machine learning, GraphX for graphs, Spark SQL for database-style queries) that saves you from having to glue together five different tools.&lt;/p&gt;

&lt;p&gt;If you're coming from my history with infrastructure — Linux, networking, servers — you'll quickly understand why Spark earns my respect: it's not magic, it's distributed systems engineering done right, with two decades of academic papers behind it (the original RDD paper by Zaharia et al. is required reading if you like understanding the &lt;em&gt;why&lt;/em&gt;, not just the &lt;em&gt;what&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to use it
&lt;/h2&gt;

&lt;p&gt;Now for the honest part, because in this series we don't sell smoke: if your dataset fits comfortably in a single machine's RAM (say, under 10-20 GB depending on the hardware), setting up a Spark cluster is like using a crane to lift a soda can. Pandas, Polars, or even DuckDB will give you faster results with a fraction of the operational complexity.&lt;/p&gt;

&lt;p&gt;It's also not the best option if you need real sub-second latency in streaming — there, Apache Flink beats it hands down because it processes event by event, not in micro-batches. And if your team doesn't have experience operating distributed clusters (YARN, Kubernetes, or Databricks as a managed layer), the maintenance cost can eat up any performance gain. Spark isn't "install and go": it requires understanding partitioning, shuffle, executor memory — things you learn the hard way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;This is post #13 of "Awesome Curated: The Tools," the series where we break down tools that passed the filter of our curation system — cross-referencing awesome lists, AI analysis, and a final human verdict. Spark is one of those tools that doesn't have the shine of something new, but that's still there, carrying the real weight of the industry, long after the hype moved on to something else.&lt;/p&gt;

&lt;p&gt;If you're interested in the infrastructure and systems side of things, check out the post on &lt;a href="https://juanchi.dev/en/blog/nodejs-runtime-that-changed-backend-forever" rel="noopener noreferrer"&gt;Node.js and the runtime that changed the backend&lt;/a&gt; or the one on &lt;a href="https://juanchi.dev/en/blog/sniffnet-monitor-network-traffic-without-tcpdump" rel="noopener noreferrer"&gt;Sniffnet for monitoring your network&lt;/a&gt;. And if you want to see the full arc of the series, the complete list is at &lt;a href="https://juanchi.dev/en/blog/series/awesome-curated-tools" rel="noopener noreferrer"&gt;/blog/series/awesome-curated-tools&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://juanchi.dev/en/blog/apache-spark-distributed-big-data-streaming-engine" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>english</category>
      <category>java</category>
      <category>bigdata</category>
      <category>spark</category>
    </item>
    <item>
      <title>Apache Spark procesa terabytes sin que se te derrita el cluster</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:00:18 +0000</pubDate>
      <link>https://dev.to/jtorchia/apache-spark-procesa-terabytes-sin-que-se-te-derrita-el-cluster-258j</link>
      <guid>https://dev.to/jtorchia/apache-spark-procesa-terabytes-sin-que-se-te-derrita-el-cluster-258j</guid>
      <description>&lt;p&gt;Che, antes de arrancar tengo que hacer una aclaración honesta, porque en esta serie la transparencia es sagrada: el análisis automático que me pasaron para este post hablaba de "memory layer para AI agents", vector DBs y persistencia de contexto cross-session. Eso no tiene nada que ver con lo que es realmente Apache Spark. Esa descripción corresponde a otra herramienta — probablemente algo como Mem0 o similar — y se cruzó mal en el pipeline de curación. Pasa, los sistemas automáticos a veces mezclan metadata. Por eso el veredicto humano todavía dice "pendiente": acá está, hecho a mano, con la data real.&lt;/p&gt;

&lt;p&gt;Lo que sí tengo confirmado: Apache Spark aparece en &lt;strong&gt;3 awesome lists independientes&lt;/strong&gt; y tiene años de historia en producción en empresas que procesan volúmenes enormes de datos. La descripción oficial del repo lo define como un framework de "micro-batch processing for streams", con "stateful exactly-once semantics". En criollo: es el motor que entra en escena cuando tu volumen de datos supera lo que un solo servidor puede manejar en memoria, sin importar cuánta RAM le metas.&lt;/p&gt;

&lt;p&gt;Pensalo así: tenés logs de una app que generan 500 GB por día, y necesitás calcular métricas agregadas — usuarios únicos, latencias promedio, detección de anomalías — sin esperar 14 horas a que termine un script de Python corriendo en un solo core. Ahí es donde Spark deja de ser una curiosidad académica y se convierte en la herramienta que te salva el sprint.&lt;/p&gt;

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

&lt;p&gt;Apache Spark es un motor de procesamiento distribuido de datos, escrito originalmente en Scala, que corre sobre la JVM (sí, otra vez la JVM — por algo la menciono tanto en esta serie). Nació en el AMPLab de Berkeley como respuesta directa a las limitaciones de Hadoop MapReduce: Spark procesa en memoria en vez de escribir a disco en cada paso intermedio, lo que lo hace órdenes de magnitud más rápido para cargas de trabajo iterativas (pensá en algoritmos de ML, que necesitan pasar por los mismos datos una y otra vez).&lt;/p&gt;

&lt;p&gt;El concepto central es el &lt;strong&gt;RDD&lt;/strong&gt; (Resilient Distributed Dataset): una colección de datos particionada entre los nodos del cluster, inmutable, y que sabe reconstruirse sola si un nodo se cae (de ahí lo de "resilient"). Sobre eso se montan capas más amigables como DataFrames y Datasets, que te dan una API tipo SQL/Pandas pero distribuida.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Ejemplo clásico: contar palabras en un dataset gigante&lt;/span&gt;
&lt;span class="c1"&gt;// distribuido entre todos los nodos del cluster&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.spark.sql.SparkSession&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;spark&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;SparkSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;appName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ContadorDePalabras"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;getOrCreate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;textFile&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;spark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;read&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;textFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hdfs://logs/*.txt"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;conteo&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;textFile&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;linea&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;// separamos en palabras&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;groupByKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                  &lt;span class="c1"&gt;// agrupamos por palabra&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;count&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;                               &lt;span class="c1"&gt;// contamos ocurrencias&lt;/span&gt;

&lt;span class="nv"&gt;conteo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;show&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nv"&gt;spark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo interesante es que ese mismo código corre igual en tu notebook con 4 GB de RAM que en un cluster de 200 nodos en AWS EMR. Spark se encarga de particionar los datos, distribuir las tareas, y manejar los fallos — vos solo describís &lt;em&gt;qué&lt;/em&gt; querés hacer, no &lt;em&gt;cómo&lt;/em&gt; distribuirlo.&lt;/p&gt;

&lt;p&gt;La parte de streaming (la que mencionaba la descripción oficial del repo, "micro-batch processing for streams... stateful exactly-once semantics") es Spark Structured Streaming: en vez de procesar evento por evento como Kafka Streams o Flink, Spark agrupa los eventos en micro-batches (cada pocos segundos) y los procesa con la misma API que usás para batch. Eso te da "exactly-once" — ningún evento se procesa dos veces ni se pierde, ni aunque un nodo explote en el peor momento.&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="c1"&gt;# Streaming en PySpark: contamos eventos por ventana de tiempo
# leyendo de un topic de Kafka en tiempo real
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readStream&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kafka&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kafka.bootstrap.servers&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;localhost:9092&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subscribe&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;eventos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;conteoPorVentana&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1 minute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conteoPorVentana&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeStream&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;awaitTermination&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El proyecto es open source bajo licencia Apache 2.0, mantenido por la Apache Software Foundation, con soporte oficial para Scala, Java, Python (PySpark) y R. El repo está en &lt;a href="https://github.com/apache/spark" rel="noopener noreferrer"&gt;github.com/apache/spark&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué está en la lista
&lt;/h2&gt;

&lt;p&gt;Que aparezca en 3 awesome lists independientes es un dato a favor, aunque no deja de ser una métrica de curación, no una prueba de calidad técnica en sí misma. Lo que sí puedo decir con base en la documentación oficial y años de adopción documentada en la industria: la combinación de velocidad en memoria, una API unificada para batch y streaming, y un ecosistema de librerías integradas (MLlib para machine learning distribuido, GraphX para grafos, Spark SQL para queries estilo base de datos) es lo que lo diferencia de alternativas como Hadoop MapReduce (su predecesor directo, mucho más lento porque escribe a disco en cada paso) o Dask (más pythonico pero con un ecosistema y madurez muy por debajo).&lt;/p&gt;

&lt;p&gt;Si venís de mi historia con infraestructura — Linux, redes, servidores — vas a entender rápido por qué Spark me genera respeto: no es magia, es ingeniería de sistemas distribuidos bien hecha, con dos décadas de papers académicos atrás (el paper original de RDDs de Zaharia et al. es lectura obligada si te gusta entender el &lt;em&gt;por qué&lt;/em&gt;, no solo el &lt;em&gt;qué&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuándo NO usarlo
&lt;/h2&gt;

&lt;p&gt;Ahora la parte honesta, porque en esta serie no vendemos humo: si tu dataset entra cómodo en la RAM de una sola máquina (digamos, menos de 10-20 GB dependiendo del hardware), montar un cluster de Spark es usar una grúa para levantar una lata de gaseosa. Pandas, Polars o incluso DuckDB te van a dar resultados más rápido y con una fracción de la complejidad operativa.&lt;/p&gt;

&lt;p&gt;Tampoco es la mejor opción si necesitás latencia sub-segundo real en streaming — ahí Apache Flink le gana de mano porque procesa evento por evento, no en micro-batches. Y si tu equipo no tiene experiencia operando clusters distribuidos (YARN, Kubernetes, o Databricks como capa managed), el costo de mantenimiento puede comerte cualquier ganancia de performance. Spark no es "instalá y listo": requiere entender particionamiento, shuffle, memoria de executors — cosas que se aprenden a los golpes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;Este es el post #13 de "Awesome Curated: The Tools", la serie donde desmenuzamos herramientas que pasaron el filtro de nuestro sistema de curación — cross-referencia entre awesome lists, análisis de IA, y veredicto humano final. Spark es de esas herramientas que no tienen el brillo de lo nuevo, pero que siguen ahí, aguantando el peso real de la industria, mucho después de que el hype se fue a otra parte.&lt;/p&gt;

&lt;p&gt;Si te interesó el lado de infraestructura y sistemas, dale una vuelta al post sobre &lt;a href="https://juanchi.dev/es/blog/nodejs-runtime-javascript-backend-event-loop-ecosystem" rel="noopener noreferrer"&gt;Node.js y el runtime que cambió el backend&lt;/a&gt; o al de &lt;a href="https://juanchi.dev/es/blog/sniffnet-monitor-trafico-red-ui-accesible-rust" rel="noopener noreferrer"&gt;Sniffnet para monitorear tu red&lt;/a&gt;. Y si querés ver el arco completo de la serie, la lista entera está en &lt;a href="https://juanchi.dev/es/blog/series/awesome-curated-tools" rel="noopener noreferrer"&gt;/blog/series/awesome-curated-tools&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Este artículo fue publicado originalmente en &lt;a href="https://juanchi.dev/es/blog/apache-spark-procesamiento-distribuido-big-data-streaming" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>java</category>
      <category>bigdata</category>
    </item>
    <item>
      <title>I Gave My VS Code Extension to a Cybersecurity Model and It Found an Infinite Hang</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Thu, 08 Oct 2026 01:01:59 +0000</pubDate>
      <link>https://dev.to/jtorchia/i-gave-my-vs-code-extension-to-a-cybersecurity-model-and-it-found-an-infinite-hang-5e03</link>
      <guid>https://dev.to/jtorchia/i-gave-my-vs-code-extension-to-a-cybersecurity-model-and-it-found-an-infinite-hang-5e03</guid>
      <description>&lt;p&gt;CertView is a VS Code extension I wrote to inspect certificates: you open a &lt;code&gt;.pem&lt;/code&gt;, a &lt;code&gt;.p12&lt;/code&gt;, a &lt;code&gt;.cer&lt;/code&gt;, and it shows you the subject, the dates, the fingerprint, all offline. It's published on the Marketplace. It parses binary files that anyone can send you, so it's attack surface, and until this week I'd never looked at it thinking like an attacker.&lt;/p&gt;

&lt;p&gt;I now have access to Daybreak Blue, OpenAI's defensive cyber model lane. I gave it my own code, the one I maintain, and asked it one concrete thing: assume I send you a malicious certificate and Juan opens it in the editor, what breaks? It found two ways to hang VS Code with a file smaller than a phone photo. I fixed both before writing this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PEM That Takes 88 Seconds to Do Nothing
&lt;/h2&gt;

&lt;p&gt;The PEM parser splits the file into blocks. For every line it read, it re-joined the entire accumulated block to measure its size. One &lt;code&gt;join&lt;/code&gt; per line. That's O(n²): double the lines, quadruple the work.&lt;/p&gt;

&lt;p&gt;I measured it on the actual code. A PEM block with many single-character lines, staying under the 256 KiB limit the plugin itself enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;39 KiB → 2.4 seconds&lt;/li&gt;
&lt;li&gt;78 KiB → 9.8 seconds&lt;/li&gt;
&lt;li&gt;156 KiB → 39.7 seconds&lt;/li&gt;
&lt;li&gt;234 KiB → &lt;strong&gt;87.7 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The size limit didn't help, because it was checked &lt;em&gt;after&lt;/em&gt; re-joining all the lines. The fix is a one-line idea: keep a length counter and do a single &lt;code&gt;join&lt;/code&gt; when closing the block. O(n) instead of O(n²).&lt;/p&gt;

&lt;h2&gt;
  
  
  The PKCS#12 That Never Ends
&lt;/h2&gt;

&lt;p&gt;This one is worse, and it's the mess that made me stop.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.p12&lt;/code&gt; file carries a number inside: how many iterations to use to derive the key from the password. It's a legitimate mechanism — more iterations, more expensive to brute-force. The problem is that CertView passed that number straight to the parsing library (&lt;code&gt;node-forge&lt;/code&gt;) exactly as it came from the file, with no cap, and the derivation runs synchronously: while it runs, VS Code doesn't respond.&lt;/p&gt;

&lt;p&gt;An attacker can declare a huge number. And here's the detail that turns it into a hang instead of a delay: &lt;code&gt;node-forge&lt;/code&gt; reads that number with &lt;code&gt;parseInt&lt;/code&gt;. A 128-byte integer of &lt;code&gt;0xFF&lt;/code&gt; inside the file converts, in JavaScript, into &lt;code&gt;Infinity&lt;/code&gt;. The derivation loop is &lt;code&gt;for (round = 0; round &amp;lt; iterations; round++)&lt;/code&gt;. With &lt;code&gt;iterations = Infinity&lt;/code&gt;, it's not that it takes a long time: it never ends.&lt;/p&gt;

&lt;p&gt;I verified it in one line of Node:&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="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the worst part: CertView automatically tries the empty password as soon as you open the file, before asking you anything. So the attacker doesn't need to know any password. You open the &lt;code&gt;.p12&lt;/code&gt; to see what it is, and the editor freezes on you forever.&lt;/p&gt;

&lt;p&gt;The fix is a preflight: before touching the library, CertView now reads the iteration numbers directly from the file structure — no &lt;code&gt;parseInt&lt;/code&gt;, so there's no path to &lt;code&gt;Infinity&lt;/code&gt; — and rejects anything above 100,000. If the file asks for more, it doesn't get parsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Didn't Find, Which Also Matters
&lt;/h2&gt;

&lt;p&gt;Not everything was a finding. The model reviewed the webview (the view where certificate data is shown) and confirmed that hostile fields are properly escaped, that the Content-Security-Policy is restrictive, that passwords aren't logged or saved, and that encrypted private keys aren't even decrypted. Instead of inventing vulnerabilities to pad out a list, it said where the code was already fine. That's what makes it useful: a report that's pure findings doesn't let you know what it checked and ruled out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sol vs. Blue, Same Code
&lt;/h2&gt;

&lt;p&gt;I ran the same audit, with the exact same request word for word, on two models: GPT-5.6-Sol (the general-purpose one) and Daybreak Blue (the defensive one). Both read the code, neither invented anything. The difference wasn't that Blue "did something forbidden": it was depth. Sol saw that the PKCS#12 "freezes the host"; Blue also saw the &lt;code&gt;Infinity&lt;/code&gt; case, which is the difference between slow and non-terminating. And Blue found one more thing Sol didn't: a race window between measuring the file size and reading it.&lt;/p&gt;

&lt;p&gt;That's the honest part of the experiment. This was defensive work on my own code, and for that the general-purpose model also does the job. The specialized lane showed up in the detail, not in unlocking something the other refused to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Obstacle Wasn't the Model
&lt;/h2&gt;

&lt;p&gt;Two things I didn't expect, and I'm mentioning them because they're the part that doesn't make it into the announcement:&lt;/p&gt;

&lt;p&gt;To get the account enabled I had to verify identity and set a physical YubiKey as the sole login — one single key, the one that now opens all my work. It's not optional, and it makes sense: a model with the brakes taken off for security work is exactly what someone would want to use with a stolen account.&lt;/p&gt;

&lt;p&gt;And on the day I ran the audit, what slowed me down most wasn't either model: it was the tool's sandbox, which stopped being able to isolate the network on that machine and blocked all file reads. The model was ready; the plumbing around it wasn't. It's almost always like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do If You Use CertView
&lt;/h2&gt;

&lt;p&gt;Update to 0.5.1. Both vulnerabilities are fixed there, with tests that fail if either comes back. And if you're writing a parser for something sent to you from outside: measure your limits &lt;em&gt;before&lt;/em&gt; doing the expensive work, not after, and never pass a library a number that came from the file without a cap.&lt;/p&gt;

</description>
      <category>english</category>
      <category>experiments</category>
      <category>openai</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Le di mi extensión de VS Code a un modelo de ciberseguridad y encontró un cuelgue infinito</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Thu, 08 Oct 2026 01:01:54 +0000</pubDate>
      <link>https://dev.to/jtorchia/le-di-mi-extension-de-vs-code-a-un-modelo-de-ciberseguridad-y-encontro-un-cuelgue-infinito-2hfi</link>
      <guid>https://dev.to/jtorchia/le-di-mi-extension-de-vs-code-a-un-modelo-de-ciberseguridad-y-encontro-un-cuelgue-infinito-2hfi</guid>
      <description>&lt;p&gt;CertView es una extensión de VS Code que escribí para inspeccionar certificados: abrís un &lt;code&gt;.pem&lt;/code&gt;, un &lt;code&gt;.p12&lt;/code&gt;, un &lt;code&gt;.cer&lt;/code&gt;, y te muestra el sujeto, las fechas, el fingerprint, todo offline. Está publicada en el Marketplace. Parsea archivos binarios que te puede mandar cualquiera, así que es superficie de ataque, y hasta esta semana nunca la había mirado pensando como atacante.&lt;/p&gt;

&lt;p&gt;Ahora tengo acceso a Daybreak Blue, el carril defensivo del modelo cyber de OpenAI. Le di mi propio código, el que mantengo yo, y le pedí una cosa concreta: asumí que te mando un certificado malicioso y Juan lo abre en el editor, ¿qué se rompe? Encontró dos formas de colgar VS Code con un archivo que pesa menos que una foto del celular. Las dos las arreglé antes de escribir esto.&lt;/p&gt;

&lt;h2&gt;
  
  
  El PEM que tarda 88 segundos en no hacer nada
&lt;/h2&gt;

&lt;p&gt;El parser de PEM separa el archivo en bloques. Por cada línea que leía, volvía a pegar todo el bloque acumulado para medir su tamaño. Un &lt;code&gt;join&lt;/code&gt; por línea. Eso es O(n²): si duplicás las líneas, cuadruplicás el trabajo.&lt;/p&gt;

&lt;p&gt;Lo medí sobre el código real. Un bloque PEM con muchas líneas de un solo carácter, por debajo del límite de 256 KiB que el propio plugin impone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;39 KiB → 2,4 segundos&lt;/li&gt;
&lt;li&gt;78 KiB → 9,8 segundos&lt;/li&gt;
&lt;li&gt;156 KiB → 39,7 segundos&lt;/li&gt;
&lt;li&gt;234 KiB → &lt;strong&gt;87,7 segundos&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El límite de tamaño no ayudaba, porque se chequeaba &lt;em&gt;después&lt;/em&gt; de volver a pegar todas las líneas. El arreglo es de una línea de idea: llevar un contador de largo y hacer un solo &lt;code&gt;join&lt;/code&gt; al cerrar el bloque. O(n) en lugar de O(n²).&lt;/p&gt;

&lt;h2&gt;
  
  
  El PKCS#12 que no termina nunca
&lt;/h2&gt;

&lt;p&gt;Este es peor, y es el quilombo que me hizo parar.&lt;/p&gt;

&lt;p&gt;Un archivo &lt;code&gt;.p12&lt;/code&gt; trae adentro un número: cuántas iteraciones usar para derivar la clave desde la contraseña. Es un mecanismo legítimo — más iteraciones, más caro de atacar por fuerza bruta. El problema es que CertView le pasaba ese número a la librería de parsing (&lt;code&gt;node-forge&lt;/code&gt;) tal cual venía del archivo, sin ningún tope, y la derivación corre de forma síncrona: mientras corre, VS Code no responde.&lt;/p&gt;

&lt;p&gt;Un atacante puede declarar un número enorme. Y acá está el detalle que lo vuelve un cuelgue y no una demora: &lt;code&gt;node-forge&lt;/code&gt; lee ese número con &lt;code&gt;parseInt&lt;/code&gt;. Un entero de 128 bytes de &lt;code&gt;0xFF&lt;/code&gt; dentro del archivo se convierte, en JavaScript, en &lt;code&gt;Infinity&lt;/code&gt;. El bucle de derivación es &lt;code&gt;for (round = 0; round &amp;lt; iteraciones; round++)&lt;/code&gt;. Con &lt;code&gt;iteraciones = Infinity&lt;/code&gt;, no es que tarda mucho: no termina jamás.&lt;/p&gt;

&lt;p&gt;Lo verifiqué en una línea de Node:&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="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y lo peor: CertView prueba la contraseña vacía automáticamente apenas abrís el archivo, antes de preguntarte nada. Así que el atacante no necesita saber ninguna contraseña. Vos abrís el &lt;code&gt;.p12&lt;/code&gt; para ver qué es, y el editor se te congela para siempre.&lt;/p&gt;

&lt;p&gt;El arreglo es un preflight: antes de tocar la librería, CertView ahora lee los números de iteraciones directamente de la estructura del archivo — sin &lt;code&gt;parseInt&lt;/code&gt;, así que no hay camino a &lt;code&gt;Infinity&lt;/code&gt; — y rechaza cualquiera por encima de 100.000. Si el archivo pide más, no se parsea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que no encontró, que también importa
&lt;/h2&gt;

&lt;p&gt;No todo era un hallazgo. El modelo revisó el webview (la vista donde se muestran los datos del certificado) y confirmó que los campos hostiles se escapan bien, que la Content-Security-Policy es restrictiva, que las contraseñas no se loguean ni se guardan, y que las llaves privadas cifradas ni se descifran. En vez de inventar vulnerabilidades para llenar una lista, dijo dónde el código ya estaba bien. Eso es lo que lo hace útil: un reporte que es puro hallazgo no te deja saber qué revisó y descartó.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sol contra Blue, mismo código
&lt;/h2&gt;

&lt;p&gt;Corrí la misma auditoría, con el mismo pedido palabra por palabra, en dos modelos: GPT-5.6-Sol (el de uso general) y Daybreak Blue (el defensivo). Los dos leyeron el código, ninguno inventó nada. La diferencia no fue que Blue "hiciera algo prohibido": fue profundidad. Sol vio que el PKCS#12 "congela el host"; Blue vio además el caso &lt;code&gt;Infinity&lt;/code&gt;, que es la diferencia entre lento y no-terminante. Y Blue encontró una cosa más que Sol no: una ventana de carrera entre medir el tamaño del archivo y leerlo.&lt;/p&gt;

&lt;p&gt;Esa es la parte honesta del experimento. Esto era trabajo defensivo sobre mi propio código, y para eso el modelo de uso general también sirve. El carril especializado se notó en el detalle, no en destrabar algo que el otro se negaba a hacer.&lt;/p&gt;

&lt;h2&gt;
  
  
  El obstáculo real no fue el modelo
&lt;/h2&gt;

&lt;p&gt;Dos cosas que no esperaba, y que cuento porque son la parte que no sale en el anuncio:&lt;/p&gt;

&lt;p&gt;Para tener la cuenta habilitada tuve que validar identidad y dejar una YubiKey física como único login — una sola llave, la que ahora abre todo mi laburo. No es opcional, y tiene sentido: un modelo al que le sacaron frenos para trabajo de seguridad es exactamente lo que alguien querría usar con una cuenta robada.&lt;/p&gt;

&lt;p&gt;Y el día que corrí la auditoría, lo que más me frenó no fue ninguno de los dos modelos: fue el sandbox de la herramienta, que dejó de poder aislar la red en esa máquina y bloqueó toda lectura de archivos. El modelo estaba listo; el plumbing alrededor, no. Casi siempre es así.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué hacer si usás CertView
&lt;/h2&gt;

&lt;p&gt;Actualizá a la 0.5.1. Las dos vulnerabilidades están corregidas ahí, con tests que fallan si alguna vuelve. Y si vos escribís un parser de algo que te mandan de afuera: medí tus límites &lt;em&gt;antes&lt;/em&gt; de hacer el laburo caro, no después, y nunca le pases a una librería un número que vino del archivo sin un tope.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>experimentos</category>
      <category>openai</category>
    </item>
    <item>
      <title>Docker HEALTHCHECK: what the command actually runs and what it doesn't tell you</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:00:22 +0000</pubDate>
      <link>https://dev.to/jtorchia/docker-healthcheck-what-the-command-actually-runs-and-what-it-doesnt-tell-you-j3k</link>
      <guid>https://dev.to/jtorchia/docker-healthcheck-what-the-command-actually-runs-and-what-it-doesnt-tell-you-j3k</guid>
      <description>&lt;p&gt;A Docker HEALTHCHECK answers a single question: did the command you defined exit with code 0? Nothing more. It doesn't measure whether the app is "healthy" in any broad sense — it measures whether that specific command, at that specific moment, didn't fail.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/reference/dockerfile/#healthcheck" rel="noopener noreferrer"&gt;official Docker documentation&lt;/a&gt; says it plainly: the &lt;code&gt;HEALTHCHECK&lt;/code&gt; instruction tells Docker how to test a container to check that it's "still working." The example the docs themselves give is telling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=5m --timeout=3s \&lt;/span&gt;
  CMD curl -f http://localhost/ || exit 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;curl -f http://localhost/&lt;/code&gt; tests one thing: that the web server answers something on the root path within 3 seconds. It doesn't test whether the database is up, whether the message queue has live workers, or whether the authentication service the app needs to log users in is responding. If curl gets any response at all — even an error page with a misconfigured 200 status — the healthcheck passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three states and what triggers each one
&lt;/h2&gt;

&lt;p&gt;According to the docs, a container with a healthcheck defined has a health status in addition to its normal status. It starts as &lt;code&gt;starting&lt;/code&gt;. Every time the check passes, it moves to &lt;code&gt;healthy&lt;/code&gt;. After a certain number of consecutive failures, it moves to &lt;code&gt;unhealthy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The command's exit code is the only thing that drives that state machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: healthy — the container is "healthy" according to that command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt;: unhealthy — the container isn't working correctly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt;: reserved, do not use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all Docker knows. It doesn't interpret the content of the response, it doesn't validate business logic, it doesn't check transitive dependencies. If your check command is &lt;code&gt;curl -f http://localhost/health&lt;/code&gt; and that endpoint returns &lt;code&gt;200 OK&lt;/code&gt; with a hardcoded JSON that never changes, you'll have a container that's forever &lt;code&gt;healthy&lt;/code&gt; even if the app's actual logic is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shallow liveness vs. real health
&lt;/h2&gt;

&lt;p&gt;Here's the distinction that matters when deciding what to put in the healthcheck: a shallow "liveness" check only confirms that the process responds — that it's not hung, that it hasn't entered an infinite loop, that the port is listening. A real health check validates that the dependencies the app needs to serve traffic actually work: database connection, access to critical external services, queue status if the app depends on them.&lt;/p&gt;

&lt;p&gt;The docs give the exact example of the problem a basic liveness check solves: "detecting cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server process is still running." That's exactly what it covers, no more, no less. If the process responds but can't write to the database because the connection dropped, a healthcheck that just curls &lt;code&gt;/&lt;/code&gt; will never find out — the server keeps answering with 200 on that path while every real request to the app fails.&lt;/p&gt;

&lt;p&gt;A HEALTHCHECK that only verifies the process responds gives a false sense of security if it doesn't validate the app's real dependencies. The &lt;code&gt;healthy&lt;/code&gt; status in &lt;code&gt;docker ps&lt;/code&gt; can coexist perfectly well with an app that can't complete a single useful operation.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;stateDiagram-v2
  [*] --&amp;gt; starting
  starting --&amp;gt; healthy: exit 0
  starting --&amp;gt; unhealthy: consecutive failures
  healthy --&amp;gt; unhealthy: consecutive failures
  unhealthy --&amp;gt; healthy: exit 0&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  What happens with start_period and retries
&lt;/h2&gt;

&lt;p&gt;Two &lt;code&gt;HEALTHCHECK&lt;/code&gt; options define how much patience Docker has before declaring &lt;code&gt;unhealthy&lt;/code&gt;: &lt;code&gt;--start-period&lt;/code&gt; and &lt;code&gt;--retries&lt;/code&gt;. The docs explain that the start period gives initialization time for containers that need to bootstrap — if the check fails during that window, it doesn't count toward the maximum number of retries. But if the check passes once during the start period, the container is considered started, and from then on every consecutive failure does count.&lt;/p&gt;

&lt;p&gt;This matters in a detail the docs don't highlight but that follows from the mechanics: if the app takes a while to come up and your start_period is short, you can end up counting as a "real failure" something that was actually "still starting up." The right tuning of start_period and retries depends on the app's actual bootstrap time — there's no universal value that works for every image.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't measure, even if the healthcheck is well written
&lt;/h2&gt;

&lt;p&gt;Not even the best &lt;code&gt;CMD&lt;/code&gt; inside &lt;code&gt;HEALTHCHECK&lt;/code&gt; solves this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External dependencies you're not explicitly checking.&lt;/strong&gt; If the command doesn't touch the database, the healthcheck knows nothing about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial degradation.&lt;/strong&gt; A service that responds slowly but within the timeout passes the same as one that responds instantly. The exit code has no gradients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business state.&lt;/strong&gt; If the app returns 200 but with corrupted data or an empty response where there should be content, and your check only validates the HTTP code, it passes all the same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything happening outside the container.&lt;/strong&gt; HEALTHCHECK runs inside the container, with whatever visibility it has from there. It doesn't see the state of the host, the external network, or other containers unless your command queries them directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these points gets solved by adding logic to the healthcheck command — not by changing how Docker interprets the result. If you need the check to reflect the service's real health, the work is in writing an endpoint or a script that actually tests the dependencies that matter, not in tweaking intervals or retries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/reference/dockerfile/#healthcheck" rel="noopener noreferrer"&gt;Docker Docs — Dockerfile reference, HEALTHCHECK instruction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://juanchi.dev/en/blog/docker-healthcheck-what-it-measures-and-what-it-doesnt" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>english</category>
      <category>docker</category>
      <category>devops</category>
      <category>dockercompose</category>
    </item>
    <item>
      <title>Docker HEALTHCHECK: qué comando corre y qué no te dice</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:00:17 +0000</pubDate>
      <link>https://dev.to/jtorchia/docker-healthcheck-que-comando-corre-y-que-no-te-dice-423o</link>
      <guid>https://dev.to/jtorchia/docker-healthcheck-que-comando-corre-y-que-no-te-dice-423o</guid>
      <description>&lt;p&gt;Un HEALTHCHECK de Docker contesta una sola pregunta: ¿el comando que definiste salió con código 0? Nada más. No mide si la app está "sana" en ningún sentido amplio — mide si ese comando específico, en ese momento, no falló.&lt;/p&gt;

&lt;p&gt;La &lt;a href="https://docs.docker.com/reference/dockerfile/#healthcheck" rel="noopener noreferrer"&gt;documentación oficial de Docker&lt;/a&gt; lo dice sin vueltas: la instrucción &lt;code&gt;HEALTHCHECK&lt;/code&gt; le dice a Docker cómo testear un contenedor para chequear que "sigue funcionando". El ejemplo que da la propia doc es elocuente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=5m --timeout=3s \&lt;/span&gt;
  CMD curl -f http://localhost/ || exit 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese &lt;code&gt;curl -f http://localhost/&lt;/code&gt; prueba una cosa: que el servidor web contesta algo en la ruta raíz dentro de 3 segundos. No prueba que la base de datos esté arriba, que la cola de mensajes tenga workers vivos, ni que el servicio de autenticación que la app necesita para loguear usuarios esté respondiendo. Si curl recibe cualquier respuesta — incluso una página de error con status 200 mal configurado — el healthcheck pasa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los tres estados y qué dispara cada uno
&lt;/h2&gt;

&lt;p&gt;Según la doc, un contenedor con healthcheck definido tiene un estado de salud además de su estado normal. Arranca en &lt;code&gt;starting&lt;/code&gt;. Cada vez que el chequeo pasa, pasa a &lt;code&gt;healthy&lt;/code&gt;. Después de una cierta cantidad de fallos consecutivos, pasa a &lt;code&gt;unhealthy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El exit code del comando es lo único que mueve esa máquina de estados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: healthy — el contenedor está "sano" según ese comando&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt;: unhealthy — el contenedor no funciona correctamente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt;: reservado, no usar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eso es todo lo que Docker sabe. No interpreta el contenido de la respuesta, no valida lógica de negocio, no chequea dependencias transitivas. Si tu comando de chequeo es &lt;code&gt;curl -f http://localhost/health&lt;/code&gt; y ese endpoint devuelve &lt;code&gt;200 OK&lt;/code&gt; con un JSON hardcodeado que nunca cambia, vas a tener un contenedor eternamente &lt;code&gt;healthy&lt;/code&gt; aunque la lógica real de la app esté rota.&lt;/p&gt;

&lt;h2&gt;
  
  
  Liveness superficial vs salud real
&lt;/h2&gt;

&lt;p&gt;Acá está la distinción que importa para decidir qué poner en el healthcheck: un chequeo de "liveness" superficial solo confirma que el proceso responde — que no está colgado, que no entró en loop infinito, que el puerto escucha. Un chequeo de salud real valida que las dependencias que la app necesita para servir tráfico funcionan: conexión a la base de datos, acceso a servicios externos críticos, estado de colas si la app depende de ellas.&lt;/p&gt;

&lt;p&gt;La doc da el ejemplo exacto del problema que resuelve un liveness check básico: "detectar casos como un servidor web atascado en un loop infinito e incapaz de manejar nuevas conexiones, aunque el proceso del servidor siga corriendo". Eso es exactamente lo que cubre, ni más ni menos. Si el proceso responde pero no puede escribir en la base de datos porque se cortó la conexión, un healthcheck que solo pega un &lt;code&gt;curl&lt;/code&gt; a &lt;code&gt;/&lt;/code&gt; nunca se va a enterar — el servidor sigue respondiendo con 200 en esa ruta mientras cada request real a la app falla.&lt;/p&gt;

&lt;p&gt;Un HEALTHCHECK que solo verifica que el proceso responde da una falsa sensación de seguridad si no valida las dependencias reales de la app. El estado &lt;code&gt;healthy&lt;/code&gt; en &lt;code&gt;docker ps&lt;/code&gt; puede convivir perfectamente con una app que no puede completar ninguna operación útil.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;stateDiagram-v2
  [*] --&amp;gt; starting
  starting --&amp;gt; healthy: exit 0
  starting --&amp;gt; unhealthy: fallos consecutivos
  healthy --&amp;gt; unhealthy: fallos consecutivos
  unhealthy --&amp;gt; healthy: exit 0&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Qué pasa con start_period y los reintentos
&lt;/h2&gt;

&lt;p&gt;Dos opciones de &lt;code&gt;HEALTHCHECK&lt;/code&gt; definen cuánta paciencia tiene Docker antes de declarar &lt;code&gt;unhealthy&lt;/code&gt;: &lt;code&gt;--start-period&lt;/code&gt; y &lt;code&gt;--retries&lt;/code&gt;. La doc explica que el start period da tiempo de inicialización para contenedores que necesitan bootstrapear — si el chequeo falla durante esa ventana, no cuenta para el máximo de reintentos. Pero si el chequeo pasa una vez durante el start period, el contenedor se considera arrancado y desde ahí todos los fallos consecutivos sí cuentan.&lt;/p&gt;

&lt;p&gt;Esto importa en un detalle que la doc no resalta pero que se desprende de la mecánica: si la app tarda en levantar y tu start_period es corto, podés terminar contando como "fallo real" algo que en realidad era "todavía estoy arrancando". El ajuste correcto de start_period y retries depende del tiempo real de bootstrap de la app — no hay un valor universal que sirva para toda imagen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué no mide, aunque el healthcheck esté bien escrito
&lt;/h2&gt;

&lt;p&gt;Ni el mejor &lt;code&gt;CMD&lt;/code&gt; dentro de &lt;code&gt;HEALTHCHECK&lt;/code&gt; resuelve esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependencias externas que no estás chequeando explícitamente.&lt;/strong&gt; Si el comando no toca la base de datos, el healthcheck no sabe nada sobre ella.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degradación parcial.&lt;/strong&gt; Un servicio que responde lento pero dentro del timeout pasa igual que uno que responde instantáneo. El exit code no tiene gradientes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estado de negocio.&lt;/strong&gt; Si la app devuelve 200 pero con datos corruptos o una respuesta vacía donde debería haber contenido, y tu chequeo solo valida el código HTTP, pasa igual.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lo que pasa fuera del contenedor.&lt;/strong&gt; HEALTHCHECK corre dentro del contenedor, con la visibilidad que tiene desde ahí. No ve el estado del host, de la red externa, ni de otros contenedores salvo que tu comando los consulte directamente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada uno de estos puntos se resuelve agregando lógica al comando del healthcheck — no cambiando cómo Docker interpreta el resultado. Si necesitás que el chequeo refleje la salud real del servicio, el trabajo está en escribir un endpoint o un script que efectivamente pruebe las dependencias que importan, no en ajustar intervalos o reintentos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuente original
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/reference/dockerfile/#healthcheck" rel="noopener noreferrer"&gt;Docker Docs — Dockerfile reference, instrucción HEALTHCHECK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Este artículo fue publicado originalmente en &lt;a href="https://juanchi.dev/es/blog/docker-healthcheck-que-mide" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Mem0 Doesn't Fix an Unbounded Agent, It Complements It</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Sun, 27 Sep 2026 12:00:20 +0000</pubDate>
      <link>https://dev.to/jtorchia/mem0-doesnt-fix-an-unbounded-agent-it-complements-it-259h</link>
      <guid>https://dev.to/jtorchia/mem0-doesnt-fix-an-unbounded-agent-it-complements-it-259h</guid>
      <description>&lt;p&gt;I have a hard limit configured in Cline: it doesn't touch files outside a specific folder, it doesn't run network commands without confirmation, and when the session gets long, the context starts piling up garbage. I already wrote about &lt;a href="https://juanchi.dev/en/blog/sniffnet-ai-agents-network-traffic-monitoring" rel="noopener noreferrer"&gt;setting explicit limits on Cline running on autopilot&lt;/a&gt; and about monitoring what an agent does without telling you. The question that kept nagging me after nailing down that scope restriction is a different one: is lost context in long sessions a problem you solve with more scope discipline, or do you actually need an external memory layer?&lt;/p&gt;

&lt;p&gt;That's where Mem0 comes in. It's a library that promises memory persistence for AI agents — it saves relevant facts from a conversation and retrieves them later, instead of re-injecting the entire raw history on every request. The pitch sounds good for the case I care about: a Cline session that runs for hours, on the same repo, where the agent repeats questions you already answered three times because the context filled up with noise and lost what mattered.&lt;/p&gt;

&lt;p&gt;My thesis, before getting into the weeds: Mem0 can help in long, repetitive sessions, where the same kind of information gets needed over and over. But it doesn't replace the first line of defense, which is still limiting what the agent can touch and how much context you feed it going in. A memory layer is a patch on top of a design problem, not a solution to the design problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Mem0 repo says and what it doesn't say
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/mem0ai/mem0" rel="noopener noreferrer"&gt;official Mem0 GitHub repo&lt;/a&gt; describes the tool as a memory layer that lets AI agents remember preferences, facts, and context across sessions, with support for multiple vector storage backends. The core idea is simple: instead of sending the entire conversation history to the model every time, Mem0 extracts and saves relevant snippets, and retrieves them when needed.&lt;/p&gt;

&lt;p&gt;What the repo doesn't say — and this matters — is how much it reduces token consumption in a real code-agent workflow like Cline, working on a specific repo, with tool calls (reading a file, running a command, writing a diff) interleaved with reasoning. The docs' examples are built for conversational chatbots, not for agents that execute actions on a filesystem. That difference isn't trivial: a code agent generates a different kind of context than a chatbot that just talks. Diffs, terminal output, file contents — that's not "conversation memory," that's operational state.&lt;/p&gt;

&lt;p&gt;So the question I'm asking isn't "does Mem0 work?" — the repo has activity, tests, and documented use cases. The question is "does Mem0 solve the specific context bloat problem in a code agent like Cline?", and the public evidence isn't enough to answer that with certainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where people get it wrong: install Mem0 and expect magic
&lt;/h2&gt;

&lt;p&gt;The common recipe I see floating around is: you grab Mem0, wire it up to the agent, and assume context bloat disappears because "now it has memory." The hidden cost of that recipe is that Mem0 adds a new layer with its own latency — every memory query involves a vector search, and that search isn't free in time or tokens if the retrieval prompt is verbose.&lt;/p&gt;

&lt;p&gt;The counterexample that convinces me not to buy the hype without proof: if the agent already has a badly defined scope — you tell it "fix the bug" without specifying which files it can touch, without telling it what NOT to do — no memory layer fixes that. The agent will keep reading extra files, running exploratory commands, generating context it doesn't need. Mem0 can help so session 5 doesn't repeat what was already established in session 1, but it doesn't stop session 1 from being a context disaster if the scope was wrong from the start.&lt;/p&gt;

&lt;p&gt;This connects directly to something I've been saying for a while: the underlying problem with autonomous agents isn't "missing memory," it's "missing limits." A well-written &lt;code&gt;.clinerules&lt;/code&gt;, with explicit restrictions on which folders it can touch and which commands are forbidden, fixes more context bloat than an external memory layer — because it attacks the cause, not the symptom.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
  A[Long Cline session] --&amp;gt; B{Agent scope defined?}
  B --&amp;gt;|No| C[Context grows unchecked]
  B --&amp;gt;|Yes| D[Context bounded from the start]
  C --&amp;gt; E[Mem0 filters repeated noise]
  E --&amp;gt; F[Background bloat persists]
  D --&amp;gt; G[Mem0 adds real value in repetitive sessions]&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  The experiment I'd design before adopting this
&lt;/h2&gt;

&lt;p&gt;I don't have production metrics of Mem0 integrated with Cline, and I'm not going to make any up. What I can lay out is the reproducible experiment I'd run before deciding whether it's worth it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fixed test repo&lt;/strong&gt;: a small repo, with a known repetitive task (for example, adding the same type of endpoint three times with variations).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline without Mem0&lt;/strong&gt;: run the task three times in separate Cline sessions, counting context tokens per session (Cline exposes this in its cost UI).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same task with Mem0&lt;/strong&gt;: integrate Mem0 as a memory layer between sessions, repeat the three runs, and compare the input token count on the third run against the third run of the baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success criteria&lt;/strong&gt;: if the third run with Mem0 uses noticeably fewer input context tokens than the third run without Mem0, there's a real signal. If the difference is marginal, or Mem0's query overhead cancels out the savings, the tool isn't solving the problem for that case.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I didn't run this experiment myself — this is the design I'd follow before writing a post claiming numbers. Anyone who wants to validate the thesis can clone the Mem0 repo and repeat these steps with their own Cline setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision matrix: when it actually makes sense to bring in Mem0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Worth adding Mem0?&lt;/th&gt;
&lt;th&gt;What to check first&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Short sessions, single well-scoped task&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Scope already solves the bloat, memory is overhead with no return&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long sessions with repetitive tasks in the same domain&lt;/td&gt;
&lt;td&gt;Possibly&lt;/td&gt;
&lt;td&gt;Measure tokens before/after with the experiment above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent without &lt;code&gt;.clinerules&lt;/code&gt; or scope restrictions&lt;/td&gt;
&lt;td&gt;No, fix that first&lt;/td&gt;
&lt;td&gt;Fix the scope limit before adding a new layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple agents sharing project context&lt;/td&gt;
&lt;td&gt;Possibly&lt;/td&gt;
&lt;td&gt;Check if the vector backend Mem0 uses is already in your stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-off project, no continuity between sessions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Persistent memory adds nothing if there's no future session to use it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every row in this matrix is a criterion, not an absolute conclusion — the real outcome depends on the repo, the model, and how the agent is configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits: what can't be concluded without actually running this
&lt;/h2&gt;

&lt;p&gt;Without production data or real logs from a Mem0 + Cline integration, I can't claim a token reduction percentage, an added latency figure, or whether the agent's response quality improves or gets worse with persistent memory. I also can't compare Mem0 against other context-compaction strategies (like periodic manual summaries) without running both in the same scenario.&lt;/p&gt;

&lt;p&gt;What I do stand by with the available public evidence: the Mem0 repo is designed and documented first for conversational agents, not for code agents with heavy operational state. That design gap is why I wouldn't adopt the tool without first running the experiment described above on my own repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Mem0 replace limiting the scope of an agent like Cline?&lt;/strong&gt;&lt;br&gt;
No. Mem0 manages what information persists across sessions, but it doesn't control what the agent can touch within a session. Scope is still defined with explicit rules, not memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Mem0 reduce tokens on every request?&lt;/strong&gt;&lt;br&gt;
It can reduce tokens if it avoids re-injecting the full history, but it adds its own retrieval query. The net balance depends on the case — you have to measure it, not assume it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Mem0 work with Cline out of the box?&lt;/strong&gt;&lt;br&gt;
The Mem0 repo isn't built specifically for code agents with tools like Cline. Integrating it takes adaptation work, it's not plug-and-play for that use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a separate vector database to use Mem0?&lt;/strong&gt;&lt;br&gt;
Yes, Mem0 depends on a vector storage backend to index and retrieve memory. Check the repo's docs for supported options before adding new infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When does it NOT make sense to use a memory layer like this?&lt;/strong&gt;&lt;br&gt;
When the task is a single session, when the agent already has bounded scope and resolves quickly, or when the project has no continuity between runs. There, persistent memory is cost with no return.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I measure if Mem0 actually helps in my case?&lt;/strong&gt;&lt;br&gt;
With the three-run experiment described above: baseline without memory, same task with memory, comparison of input tokens on the repeated run. Without that measurement, any claim is speculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;I'm not going to recommend Mem0 as a universal fix for something that, in most cases, is an agent design problem, not a memory shortage. If you're already limiting Cline's scope with clear rules — which folders it touches, which commands are forbidden, when you require confirmation — and you still feel context degrading in long, repetitive sessions, that's where Mem0 deserves the experiment. If you haven't put that limit in place first, installing a memory layer is putting makeup on a symptom.&lt;/p&gt;

&lt;p&gt;The concrete next step for anyone who wants to actually validate this: clone the repo, set up the three-run experiment on a test project, and measure. Without that, everything else is new-tool folklore — the same mistake I've been avoiding with other pieces of the agent stack, like I wrote about &lt;a href="https://juanchi.dev/en/blog/sniffnet-ai-agents-network-traffic-monitoring" rel="noopener noreferrer"&gt;monitoring the real traffic AI agents generate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Original source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mem0 GitHub: &lt;a href="https://github.com/mem0ai/mem0" rel="noopener noreferrer"&gt;https://github.com/mem0ai/mem0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://juanchi.dev/en/blog/mem0-doesnt-fix-unbounded-agent-complements" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>english</category>
      <category>agentesia</category>
      <category>cline</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Mem0 no arregla un agente sin límites, lo complementa</title>
      <dc:creator>Juan Torchia</dc:creator>
      <pubDate>Sun, 27 Sep 2026 12:00:16 +0000</pubDate>
      <link>https://dev.to/jtorchia/mem0-no-arregla-un-agente-sin-limites-lo-complementa-3f06</link>
      <guid>https://dev.to/jtorchia/mem0-no-arregla-un-agente-sin-limites-lo-complementa-3f06</guid>
      <description>&lt;p&gt;Tengo un límite duro configurado en Cline: no toca archivos fuera de una carpeta específica, no corre comandos de red sin confirmación, y cuando la sesión se pone larga, el contexto empieza a acumular basura. Ya escribí sobre &lt;a href="https://juanchi.dev/es/blog/sniffnet-monitoreo-trafico-red-agentes-ia" rel="noopener noreferrer"&gt;poner límites explícitos a Cline en autopilot&lt;/a&gt; y sobre monitorear lo que un agente hace sin avisar. La pregunta que me quedó picando después de esa restricción de scope es otra: ¿el problema de contexto perdido en sesiones largas se resuelve con más disciplina de scope, o hace falta una capa de memoria externa?&lt;/p&gt;

&lt;p&gt;Ahí entra Mem0. Es una librería que promete persistencia de memoria para agentes de IA — guarda hechos relevantes de una conversación y los recupera después, en lugar de reinyectar todo el historial crudo en cada request. La promesa suena bien para el caso que me interesa: una sesión de Cline que dura horas, con el mismo repo, donde el agente repite preguntas que ya respondiste tres veces porque el contexto se llenó de ruido y perdió lo importante.&lt;/p&gt;

&lt;p&gt;Mi tesis, antes de meterme en el detalle: Mem0 puede ayudar en sesiones largas y repetitivas, donde el mismo tipo de información se necesita una y otra vez. Pero no reemplaza la primera línea de defensa, que sigue siendo limitar qué puede tocar el agente y cuánto contexto le das de entrada. Una capa de memoria es un parche sobre un problema de diseño, no una solución al problema de diseño.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué dice el repo de Mem0 y qué no dice
&lt;/h2&gt;

&lt;p&gt;El &lt;a href="https://github.com/mem0ai/mem0" rel="noopener noreferrer"&gt;repositorio oficial de Mem0 en GitHub&lt;/a&gt; describe la herramienta como una capa de memoria que permite a los agentes de IA recordar preferencias, hechos y contexto entre sesiones, con soporte para múltiples backends de almacenamiento vectorial. La idea central es simple: en lugar de mandar todo el historial de conversación al modelo cada vez, Mem0 extrae y guarda fragmentos relevantes, y los recupera cuando hacen falta.&lt;/p&gt;

&lt;p&gt;Lo que el repo no dice — y esto es importante — es cuánto reduce el consumo de tokens en un flujo real de agente de código como Cline, trabajando sobre un repo específico, con llamadas a herramientas (leer archivo, ejecutar comando, escribir diff) intercaladas con razonamiento. Los ejemplos de la documentación están pensados para chatbots conversacionales, no para agentes que ejecutan acciones sobre un sistema de archivos. Esa diferencia no es menor: un agente de código genera contexto de una naturaleza distinta a un chatbot que solo conversa. Diffs, salidas de terminal, contenido de archivos — eso no es "memoria de conversación", es estado operacional.&lt;/p&gt;

&lt;p&gt;Entonces la pregunta que me hago no es "¿Mem0 funciona?" — el repo tiene actividad, tests, y casos de uso documentados. La pregunta es "¿Mem0 resuelve el problema específico de context bloat en un agente de código como Cline?", y ahí la evidencia pública no alcanza para responder con certeza.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dónde se equivoca la gente: instalar Mem0 y esperar magia
&lt;/h2&gt;

&lt;p&gt;La receta común que veo circular es: agarrás Mem0, lo conectás al agente, y asumís que el context bloat desaparece porque "ahora tiene memoria". El costo oculto de esa receta es que Mem0 agrega una capa nueva con su propia latencia — cada consulta a la memoria implica una búsqueda vectorial, y esa búsqueda no es gratis en tiempo ni en tokens si el prompt de recuperación es verboso.&lt;/p&gt;

&lt;p&gt;El contraejemplo que me convence de no comprar el hype sin pruebas: si el agente ya tiene un scope mal definido — le decís "arreglá el bug" sin especificar qué archivos puede tocar, sin decirle qué NO hacer — ninguna capa de memoria arregla eso. El agente va a seguir leyendo archivos de más, ejecutando comandos exploratorios, generando contexto que no necesita. Mem0 puede ayudar a que la sesión 5 no repita lo que ya se estableció en la sesión 1, pero no evita que la sesión 1 sea un desastre de contexto si el scope está mal puesto desde el arranque.&lt;/p&gt;

&lt;p&gt;Esto conecta directo con algo que ya vengo sosteniendo: el problema de fondo en agentes autónomos no es "falta memoria", es "falta límite". Un &lt;code&gt;.clinerules&lt;/code&gt; bien escrito, con restricciones explícitas de qué carpetas puede tocar y qué comandos están prohibidos, resuelve más context bloat que una capa de memoria externa — porque ataca la causa, no el síntoma.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
  A[Sesion larga de Cline] --&amp;gt; B{Scope del agente definido?}
  B --&amp;gt;|No| C[Contexto crece sin control]
  B --&amp;gt;|Si| D[Contexto acotado desde el inicio]
  C --&amp;gt; E[Mem0 filtra ruido repetido]
  E --&amp;gt; F[Sigue habiendo bloat de fondo]
  D --&amp;gt; G[Mem0 suma valor real en sesiones repetitivas]&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  El experimento que diseñaría antes de adoptar esto
&lt;/h2&gt;

&lt;p&gt;No tengo métricas productivas de Mem0 integrado con Cline, y no las voy a inventar. Lo que sí puedo dejar es el experimento reproducible que armaría antes de decidir si vale la pena:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Repo de prueba fijo&lt;/strong&gt;: un repo chico, con una tarea repetitiva conocida (por ejemplo, agregar el mismo tipo de endpoint tres veces con variaciones).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline sin Mem0&lt;/strong&gt;: correr la tarea tres veces en sesiones separadas de Cline, contando tokens de contexto por sesión (Cline expone esto en su UI de costos).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misma tarea con Mem0&lt;/strong&gt;: integrar Mem0 como capa de memoria entre sesiones, repetir las tres corridas, y comparar el conteo de tokens de entrada en la tercera corrida contra la tercera corrida del baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criterio de éxito&lt;/strong&gt;: si la tercera corrida con Mem0 usa notablemente menos tokens de contexto de entrada que la tercera corrida sin Mem0, hay una señal real. Si la diferencia es marginal o el overhead de consulta a Mem0 compensa el ahorro, la herramienta no está resolviendo el problema para ese caso.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Este experimento no lo corrí yo — es el diseño que seguiría antes de escribir un post afirmando números. Cualquiera que quiera validar la tesis puede clonar el repo de Mem0 y repetir estos pasos con su propio setup de Cline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matriz de decisión: cuándo tiene sentido meter Mem0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situación&lt;/th&gt;
&lt;th&gt;¿Vale meter Mem0?&lt;/th&gt;
&lt;th&gt;Qué mirar primero&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sesiones cortas, tarea única y bien acotada&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;El scope ya resuelve el bloat, la memoria es overhead sin retorno&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sesiones largas con tareas repetitivas sobre el mismo dominio&lt;/td&gt;
&lt;td&gt;Posiblemente&lt;/td&gt;
&lt;td&gt;Medir tokens antes/después con el experimento de arriba&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agente sin &lt;code&gt;.clinerules&lt;/code&gt; ni restricciones de scope&lt;/td&gt;
&lt;td&gt;No, primero eso&lt;/td&gt;
&lt;td&gt;Arreglar el límite de scope antes de agregar una capa nueva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Múltiples agentes compartiendo contexto de proyecto&lt;/td&gt;
&lt;td&gt;Posiblemente&lt;/td&gt;
&lt;td&gt;Evaluar si el backend vectorial que usa Mem0 ya está en el stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proyecto de un solo uso, sin continuidad entre sesiones&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;La memoria persistente no aporta si no hay sesión futura que la use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cada fila de esta matriz es un criterio, no una conclusión absoluta — el resultado real depende del repo, del modelo y de cómo esté configurado el agente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Límites: qué no se puede concluir sin correr esto en serio
&lt;/h2&gt;

&lt;p&gt;Sin datos productivos ni logs reales de una integración Mem0 + Cline, no puedo afirmar un porcentaje de reducción de tokens, ni un tiempo de latencia agregado, ni si la calidad de las respuestas del agente mejora o empeora con memoria persistente. Tampoco puedo comparar Mem0 contra otras estrategias de compactación de contexto (como resúmenes periódicos manuales) sin correr ambas en el mismo escenario.&lt;/p&gt;

&lt;p&gt;Lo que sí sostengo con la evidencia pública disponible: el repo de Mem0 está diseñado y documentado primero para agentes conversacionales, no para agentes de código con estado operacional pesado. Esa brecha de diseño es la razón por la que no adoptaría la herramienta sin antes correr el experimento descripto arriba en un repo propio.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Mem0 reemplaza limitar el scope de un agente como Cline?&lt;/strong&gt;&lt;br&gt;
No. Mem0 gestiona qué información persiste entre sesiones, pero no controla qué puede tocar el agente dentro de una sesión. El scope se sigue definiendo con reglas explícitas, no con memoria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Mem0 reduce tokens en cada request?&lt;/strong&gt;&lt;br&gt;
Puede reducir tokens si evita reinyectar historial completo, pero agrega su propia consulta de recuperación. El balance neto depende del caso — hay que medirlo, no asumirlo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Funciona Mem0 con Cline directamente?&lt;/strong&gt;&lt;br&gt;
El repo de Mem0 no está pensado específicamente para agentes de código con herramientas como Cline. Integrarlo requiere trabajo de adaptación, no es plug-and-play para ese caso de uso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Necesito una base vectorial separada para usar Mem0?&lt;/strong&gt;&lt;br&gt;
Sí, Mem0 depende de un backend de almacenamiento vectorial para indexar y recuperar memoria. Revisá la documentación del repo para las opciones soportadas antes de sumar infraestructura nueva.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cuándo NO tiene sentido usar una capa de memoria como esta?&lt;/strong&gt;&lt;br&gt;
Cuando la tarea es de una sola sesión, cuando el agente ya tiene scope acotado y resuelve rápido, o cuando el proyecto no tiene continuidad entre corridas. Ahí la memoria persistente es costo sin retorno.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Cómo mido si Mem0 realmente ayuda en mi caso?&lt;/strong&gt;&lt;br&gt;
Con el experimento de tres corridas descripto arriba: baseline sin memoria, misma tarea con memoria, comparación de tokens de entrada en la corrida repetida. Sin esa medición, cualquier claim es especulación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mi postura
&lt;/h2&gt;

&lt;p&gt;No voy a recomendar Mem0 como solución universal a algo que en la mayoría de los casos es un problema de diseño del agente, no de falta de memoria. Si ya limitás el scope de Cline con reglas claras — qué carpetas toca, qué comandos están prohibidos, cuándo pedís confirmación — y todavía sentís que el contexto se degrada en sesiones largas y repetitivas, ahí Mem0 merece el experimento. Si no pusiste ese límite primero, instalar una capa de memoria es maquillar un síntoma.&lt;/p&gt;

&lt;p&gt;El próximo paso concreto para cualquiera que quiera validar esto en serio: clonar el repo, armar el experimento de tres corridas en un proyecto de prueba, y medir. Sin eso, todo lo demás es folklore de herramienta nueva — el mismo error que ya vengo evitando con otras piezas del stack de agentes, como escribí sobre &lt;a href="https://juanchi.dev/es/blog/sniffnet-monitoreo-trafico-red-agentes-ia" rel="noopener noreferrer"&gt;monitorear el tráfico real que generan los agentes IA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fuente original:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mem0 GitHub: &lt;a href="https://github.com/mem0ai/mem0" rel="noopener noreferrer"&gt;https://github.com/mem0ai/mem0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Este artículo fue publicado originalmente en &lt;a href="https://juanchi.dev/es/blog/mem0-memoria-agentes-ia-context-bloat-cline" rel="noopener noreferrer"&gt;juanchi.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>agentesia</category>
      <category>cline</category>
    </item>
  </channel>
</rss>
