<?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: Ritabrata Maiti</title>
    <description>The latest articles on DEV Community by Ritabrata Maiti (@ritabratamaiti).</description>
    <link>https://dev.to/ritabratamaiti</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%2F2453619%2F2ee6b749-3337-496d-8d7c-62521479edc3.jpeg</url>
      <title>DEV Community: Ritabrata Maiti</title>
      <link>https://dev.to/ritabratamaiti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ritabratamaiti"/>
    <language>en</language>
    <item>
      <title>How I built Browy, an AI agent that lives in your browser</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Mon, 19 Oct 2026 19:58:27 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/how-i-built-browy-an-ai-agent-that-lives-in-your-browser-3m5l</link>
      <guid>https://dev.to/ritabratamaiti/how-i-built-browy-an-ai-agent-that-lives-in-your-browser-3m5l</guid>
      <description>&lt;p&gt;When you send Browy its first message, Chrome puts up a bar saying that Browy&lt;br&gt;
started debugging this browser. No extension can hide it, Browy included. It&lt;br&gt;
appears because Browy drives your tabs through &lt;code&gt;chrome.debugger&lt;/code&gt;, which lets&lt;br&gt;
an extension speak the Chrome DevTools Protocol, the same protocol your own&lt;br&gt;
DevTools use.&lt;/p&gt;

&lt;p&gt;I've grown to like that bar, because it describes Browy better than I usually&lt;br&gt;
manage to. Browy is an AI agent that lives in your browser, and I mean that&lt;br&gt;
literally. It works in the Chrome, Edge or Brave window you already have open,&lt;br&gt;
signed in to everything you're signed in to.&lt;/p&gt;

&lt;p&gt;You chat with it in the side panel, or type at it in a DevTools panel, and it&lt;br&gt;
reads pages, clicks, types, fills forms and runs JavaScript in your tabs while&lt;br&gt;
you watch. The models come through your own GitHub Copilot subscription. It's&lt;br&gt;
&lt;a href="https://github.com/BrowyHQ/browy" rel="noopener noreferrer"&gt;open source&lt;/a&gt; under Apache-2.0, and I've&lt;br&gt;
been building it on my own since April. This is the long version of how it&lt;br&gt;
works, with the bugs left in.&lt;/p&gt;
&lt;h2&gt;
  
  
  What happens when you hit send
&lt;/h2&gt;

&lt;p&gt;Three programs on your computer take part, and they talk over pipes.&lt;/p&gt;

&lt;p&gt;The first is your browser, running Browy's extension: a side panel for&lt;br&gt;
chatting, a DevTools panel for people who'd rather type commands, and a&lt;br&gt;
settings page. None of them talk to the model. They talk to the extension's&lt;br&gt;
service worker, which keeps one connection open to the second program. The&lt;br&gt;
&lt;a href="https://browyhq.github.io/arch-frontend/" rel="noopener noreferrer"&gt;frontend architecture page&lt;/a&gt; walks through the extension file&lt;br&gt;
by file.&lt;/p&gt;

&lt;p&gt;That connection is Chrome's native messaging. Browy's installer registers a&lt;br&gt;
manifest under the name &lt;code&gt;com.browy.host&lt;/code&gt;, and when the service worker calls&lt;br&gt;
&lt;code&gt;chrome.runtime.connectNative('com.browy.host')&lt;/code&gt;, Chrome starts the host and&lt;br&gt;
wires the extension to its stdin and stdout. Each message is a four-byte&lt;br&gt;
length followed by JSON. The open port also keeps the service worker alive,&lt;br&gt;
which Manifest V3 would otherwise put to sleep.&lt;/p&gt;

&lt;p&gt;The host is a Node program, and it's where the agent lives. It wraps GitHub's&lt;br&gt;
&lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;Copilot SDK&lt;/a&gt;, which starts the third&lt;br&gt;
program, the &lt;a href="https://github.com/github/copilot-cli" rel="noopener noreferrer"&gt;Copilot CLI&lt;/a&gt;, in headless&lt;br&gt;
mode and talks JSON-RPC to it over another pair of pipes. The CLI makes the&lt;br&gt;
model calls on your Copilot subscription and stores each chat. Browy's code&lt;br&gt;
covers the browser half: the tools, and the picture of the page the model gets&lt;br&gt;
on every turn. The &lt;a href="https://browyhq.github.io/arch-backend/" rel="noopener noreferrer"&gt;backend architecture page&lt;/a&gt; has the&lt;br&gt;
details.&lt;/p&gt;

&lt;p&gt;That picture is rebuilt each time you send a message. The host appends a&lt;br&gt;
&lt;code&gt;&amp;lt;browser_context&amp;gt;&lt;/code&gt; block to your text, with the active tab's title and URL,&lt;br&gt;
the viewport, the scroll position and any text you've selected, followed by a&lt;br&gt;
snapshot of the page and the agent's last few tool calls. Your words go first,&lt;br&gt;
so the chat's summary in the history list shows what you asked.&lt;/p&gt;

&lt;p&gt;When the model calls a tool, the SDK runs a handler in the host. If the tool&lt;br&gt;
needs the page, the host sends a &lt;code&gt;cdp.send&lt;/code&gt; message back up the native port,&lt;br&gt;
naming a DevTools Protocol method and its parameters. The service worker picks&lt;br&gt;
the tab, attaches &lt;code&gt;chrome.debugger&lt;/code&gt; if it isn't attached yet, runs the command&lt;br&gt;
and sends the result back down. Network and console events travel the same&lt;br&gt;
route. The host coordinates, and every action happens in your browser.&lt;/p&gt;

&lt;p&gt;The DevTools panel is the same agent with a terminal in front: slash commands,&lt;br&gt;
a model picker, and a &lt;code&gt;/js&lt;/code&gt; mode that runs each line in the inspected page&lt;br&gt;
without calling the model. Each panel works on the tab it inspects. The&lt;br&gt;
&lt;a href="https://browyhq.github.io/arch-cli/" rel="noopener noreferrer"&gt;DevTools CLI page&lt;/a&gt; covers how the REPL is built.&lt;/p&gt;
&lt;h2&gt;
  
  
  The page as a numbered list
&lt;/h2&gt;

&lt;p&gt;Ask a model to click something by CSS selector and it will happily invent one.&lt;br&gt;
Even a correct selector only says where an element sat at one moment, and a&lt;br&gt;
re-render can move it.&lt;/p&gt;

&lt;p&gt;So Browy hands the model a numbered list. One &lt;code&gt;Runtime.evaluate&lt;/code&gt; call walks&lt;br&gt;
the page, shadow roots and same-origin iframes included, and keeps each&lt;br&gt;
element that's interactive, visible and within 2,000 pixels of the viewport.&lt;br&gt;
It sorts them in reading order, drops any that sit almost entirely inside&lt;br&gt;
another, stops at 150 and stamps each one with a &lt;code&gt;data-browy-id&lt;/code&gt; attribute.&lt;br&gt;
The model sees something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[1]&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;/expenses&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My expenses&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
[4]&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"e.g. Client dinner"&lt;/span&gt; &lt;span class="na"&gt;aria-name=&lt;/span&gt;&lt;span class="s"&gt;Title&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
[5]&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt; &lt;span class="na"&gt;aria-name=&lt;/span&gt;&lt;span class="s"&gt;"Amount (SGD)"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
[6]&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;checkbox&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;billable&lt;/span&gt; &lt;span class="na"&gt;unchecked&lt;/span&gt; &lt;span class="na"&gt;aria-name=&lt;/span&gt;&lt;span class="s"&gt;"Billable to client"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
[*7]&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Attach receipt&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
[~12]&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit for approval&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;~&lt;/code&gt; marks an element that's off screen, which the click tools scroll into&lt;br&gt;
view first. A &lt;code&gt;*&lt;/code&gt; marks one that wasn't in the previous snapshot, which is how&lt;br&gt;
the model notices that a dialog opened or a new part of the form appeared. The&lt;br&gt;
&lt;code&gt;aria-name&lt;/code&gt; is what a screen reader would announce, usually the label you'd&lt;br&gt;
read yourself.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;click_index(12)&lt;/code&gt;, the host finds the stamped element and sends real mouse&lt;br&gt;
events to its center with &lt;code&gt;Input.dispatchMouseEvent&lt;/code&gt;. Before and after, it&lt;br&gt;
records the URL, the title, a hash of the visible text, the number of open&lt;br&gt;
dialogs and the element's ARIA state. Half a second after the click it&lt;br&gt;
compares them and reports what changed, with flags like &lt;code&gt;urlChanged&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;domChanged&lt;/code&gt;, &lt;code&gt;modalAppeared&lt;/code&gt; and &lt;code&gt;elementStateChanged&lt;/code&gt;. If nothing changed,&lt;br&gt;
the reply says so and suggests what might be in the way, such as an overlay or&lt;br&gt;
a page that's still rendering.&lt;/p&gt;

&lt;p&gt;I didn't invent this. The indexed snapshot is modeled on&lt;br&gt;
&lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;browser-use&lt;/a&gt;'s DOM service and&lt;br&gt;
serializer, down to the rule for dropping nested elements, and I'm grateful to&lt;br&gt;
that team for working it out in the open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning tools off
&lt;/h2&gt;

&lt;p&gt;Browy has 54 browser tools, from &lt;code&gt;click_index&lt;/code&gt; and &lt;code&gt;fill_form&lt;/code&gt; to&lt;br&gt;
&lt;code&gt;get_network_requests&lt;/code&gt; and &lt;code&gt;replay_request&lt;/code&gt;. Most have a switch in Browy's&lt;br&gt;
settings. Turning one off removes it from two places, the tool definitions&lt;br&gt;
sent to the model and the SDK's &lt;code&gt;availableTools&lt;/code&gt; allowlist, so the model never&lt;br&gt;
learns it exists.&lt;/p&gt;

&lt;p&gt;Two of them run code the model writes. &lt;code&gt;evaluate_js&lt;/code&gt; runs JavaScript in&lt;br&gt;
the page, for the jobs the indexed tools can't express, and it's on.&lt;br&gt;
&lt;code&gt;run_script&lt;/code&gt; runs Node.js on your computer, so it's off until you switch it&lt;br&gt;
on. It was on by default until version 0.1.10, which was the wrong default&lt;br&gt;
for a tool that reaches your machine.&lt;/p&gt;

&lt;p&gt;The Copilot CLI also brings tools of its own, like &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;web_fetch&lt;/code&gt;,&lt;br&gt;
which act on your computer instead of the tab. Browy keeps those out of the&lt;br&gt;
allowlist until you switch them on, one at a time, the same as &lt;code&gt;run_script&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This matters because Browy reads pages written by strangers while it's signed&lt;br&gt;
in as you. Any page can carry text aimed at the model, and Browy's security&lt;br&gt;
model assumes some pages will. The switches decide what a page like that could&lt;br&gt;
talk the agent into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your own browser
&lt;/h2&gt;

&lt;p&gt;Plenty of agents start a fresh browser, often on a server, with an empty&lt;br&gt;
profile that isn't signed in to anything. For public pages at scale, that's&lt;br&gt;
the right design. Most of my repetitive work happens on pages that only exist&lt;br&gt;
because I'm signed in, though: the pull request I'm reviewing, my Gmail&lt;br&gt;
filters, an expense claim, a journal's submission portal. A fresh browser sent&lt;br&gt;
to any of them lands on a login screen. I wrote about the form-filling side of&lt;br&gt;
this in &lt;a href="https://browyhq.github.io/blog/filling-forms-signed-in/" rel="noopener noreferrer"&gt;an earlier post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Browy sidesteps that by staying in your browser. Ask about the pull request in&lt;br&gt;
front of you, and it reads the pull request in front of you, in the tab where&lt;br&gt;
you're already signed in. Your logins stay where you made them, in your&lt;br&gt;
browser profile, next to your cookies and your password manager. When a page&lt;br&gt;
wants a password or a 2FA code, the agent is meant to call &lt;code&gt;await_user&lt;/code&gt;, which&lt;br&gt;
pauses and hands the job to you. That tool's own description tells the model&lt;br&gt;
to use it instead of typing into password fields.&lt;/p&gt;

&lt;p&gt;Your chat history is stored on your machine too, under &lt;code&gt;~/.copilot/&lt;/code&gt;. What&lt;br&gt;
leaves it is what the model needs to see: your messages and the page content&lt;br&gt;
the agent reads, sent through your Copilot subscription under GitHub's terms,&lt;br&gt;
the same way the Copilot CLI sends them from a terminal.&lt;/p&gt;

&lt;p&gt;The trade is scale. Browy works in one browser on one machine, and only when&lt;br&gt;
you ask. It won't run a hundred jobs overnight, and I'm fine with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second install, and other costs
&lt;/h2&gt;

&lt;p&gt;Browy needs two programs that live outside the browser, Node for the agent and&lt;br&gt;
the Copilot CLI for the model. An extension can't install those, and native&lt;br&gt;
messaging only reaches a program that's already registered on the machine. So&lt;br&gt;
Browy installs in two steps: the extension from the Chrome Web Store, then a&lt;br&gt;
one-line command in PowerShell or a terminal. The side panel shows the command&lt;br&gt;
for your system, with a copy button, and picks up the host by itself once it's&lt;br&gt;
registered. In my testing that took 3.6 seconds. The&lt;br&gt;
&lt;a href="https://browyhq.github.io/install/" rel="noopener noreferrer"&gt;install guide&lt;/a&gt; has both commands.&lt;/p&gt;

&lt;p&gt;Having two halves also means two update schedules: the Web Store updates the&lt;br&gt;
extension, and you update the host, which the side panel prompts you to do&lt;br&gt;
when it falls behind.&lt;/p&gt;

&lt;p&gt;Then there's size. The Windows download is about 140 MB, most of it the&lt;br&gt;
Copilot CLI. In August I cut a 106 MB native binary out of every download,&lt;br&gt;
because the SDK ran the CLI as a Node script and nothing ever executed the&lt;br&gt;
binary. In October the CLI itself moved into that same package as a native&lt;br&gt;
executable. Switching to it cut the CLI's startup on Windows from 5.8 seconds&lt;br&gt;
to 0.6, and pushed the Windows download from about 80 MB back up to about 140.&lt;br&gt;
You download Browy once and wait for it to start far more often, so I'll take&lt;br&gt;
that trade.&lt;/p&gt;

&lt;p&gt;Browy is also Chromium only: Chrome, Edge and Brave. All of the above depends&lt;br&gt;
on &lt;code&gt;chrome.debugger&lt;/code&gt;, and Firefox and Safari don't give extensions an&lt;br&gt;
equivalent.&lt;/p&gt;

&lt;p&gt;And there's the debugging bar from the top of this post. It stays up while&lt;br&gt;
Browy holds a tab, and clicking Cancel on it detaches the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The system prompt that never arrived
&lt;/h2&gt;

&lt;p&gt;In October I noticed that Browy still bundled version 1.0.36 of the Copilot&lt;br&gt;
CLI, while the current release was 1.0.95, the native binary from the last&lt;br&gt;
section. Updating meant checking each SDK call Browy makes against the new&lt;br&gt;
CLI, and that turned up a bigger problem.&lt;/p&gt;

&lt;p&gt;The SDK builds its &lt;code&gt;session.create&lt;/code&gt; request from named fields, and the system&lt;br&gt;
prompt goes in one called &lt;code&gt;systemMessage&lt;/code&gt;. Browy had been passing its prompt&lt;br&gt;
as &lt;code&gt;systemPrompt&lt;/code&gt; since the first commit. Nothing complained. JavaScript&lt;br&gt;
doesn't mind an extra key on an options object, and the SDK never looked for&lt;br&gt;
that one. For five and a half months, every Browy chat ran on the Copilot&lt;br&gt;
CLI's default instructions plus Browy's tool list. The instructions that tell&lt;br&gt;
the model it's Browy, how to use the indexed tools and when to hand a sign-in&lt;br&gt;
back to you never reached it.&lt;/p&gt;

&lt;p&gt;The SDK's TypeScript types would have caught it, since &lt;code&gt;SessionConfig&lt;/code&gt; has a&lt;br&gt;
&lt;code&gt;systemMessage&lt;/code&gt; field and no &lt;code&gt;systemPrompt&lt;/code&gt;. But Browy loads the SDK with&lt;br&gt;
&lt;code&gt;require()&lt;/code&gt; and holds the client as &lt;code&gt;any&lt;/code&gt;, so the compiler never compared the&lt;br&gt;
two.&lt;/p&gt;

&lt;p&gt;The part that stings is a comment about thirty lines higher in the same&lt;br&gt;
function, shipped in the very first release. It notes that an earlier&lt;br&gt;
&lt;code&gt;includeDefaultTools: false&lt;/code&gt; flag had never been a real SDK option either. I&lt;br&gt;
had caught this exact kind of bug once and written it down, and the second one&lt;br&gt;
sat thirty lines below my note.&lt;/p&gt;

&lt;p&gt;I suspect Browy mostly behaved anyway because the tool descriptions did reach&lt;br&gt;
the model, and a lot of the guidance lives there.&lt;/p&gt;

&lt;p&gt;The fix was one line, &lt;code&gt;systemMessage: { content: SYSTEM_PROMPT }&lt;/code&gt;, with a&lt;br&gt;
catch. The prompt I'd written in April told the model to fill a form and then&lt;br&gt;
submit it, and once the prompt started arriving, that rule would have taken&lt;br&gt;
effect for the first time. So the same release rewrote it. Browy now submits&lt;br&gt;
only when you ask it to submit, send, post or buy, and otherwise stops and&lt;br&gt;
tells you the form is ready to check. Asked its name, it finally says "I'm&lt;br&gt;
Browy."&lt;/p&gt;

&lt;h2&gt;
  
  
  The chats that were on disk all along
&lt;/h2&gt;

&lt;p&gt;In May I removed my development copy of the extension to test a clean&lt;br&gt;
install, the way a new user would get it. When I opened the new copy, my older chats were&lt;br&gt;
missing from the history list. Uninstalling an extension wipes its storage, so&lt;br&gt;
I assumed they were gone.&lt;/p&gt;

&lt;p&gt;They weren't. The Copilot SDK had written every turn of every chat to its&lt;br&gt;
session store under &lt;code&gt;~/.copilot/&lt;/code&gt;, on my own disk, and the host already knew&lt;br&gt;
how to list and read those sessions. It just didn't recognize the old ones as&lt;br&gt;
Browy's. It looked for session ids starting with &lt;code&gt;sp-&lt;/code&gt; (side panel) or &lt;code&gt;dt-&lt;/code&gt;&lt;br&gt;
(DevTools), and my older chats predated that scheme.&lt;/p&gt;

&lt;p&gt;Version 0.1.4 recognizes a Browy session by any of three marks: the id prefix,&lt;br&gt;
the working directory every Browy session is created with, or the&lt;br&gt;
&lt;code&gt;&amp;lt;browser_context&amp;gt;&lt;/code&gt; block Browy adds to messages. I widened the rule for&lt;br&gt;
listing chats but left the narrow one in charge of automatic cleanup, so&lt;br&gt;
nothing of uncertain origin could be deleted by accident.&lt;/p&gt;

&lt;p&gt;Chats looked lost once more in August, when "No past chats yet" turned out to&lt;br&gt;
be a four-second timeout on a host that needed between 7.7 and 40 seconds to&lt;br&gt;
answer. Both times, the chats were fine. That's an upside of a local agent I&lt;br&gt;
hadn't planned for: when the interface said my chats were gone, I could open&lt;br&gt;
the folder and check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The form it said it had filled
&lt;/h2&gt;

&lt;p&gt;On every turn, Browy shows the model its own last five tool calls in a&lt;br&gt;
&lt;code&gt;&amp;lt;recent_actions&amp;gt;&lt;/code&gt; block, with each tool's arguments, whether it worked, how&lt;br&gt;
long it took and the start of its result. That's how the model checks whether&lt;br&gt;
its last click did anything. The same idea shows up in browser-use and in&lt;br&gt;
research agents like ReAct and SeeAct.&lt;/p&gt;

&lt;p&gt;The list lived on the host's agent object, and one agent serves every chat. So&lt;br&gt;
a new chat's first message arrived carrying the previous chat's last five&lt;br&gt;
calls.&lt;/p&gt;

&lt;p&gt;I found out on October 13, while filming the demo video that's now on Browy's&lt;br&gt;
homepage. In a brand-new chat, I asked Browy to fill the same form it had just&lt;br&gt;
filled in another chat. It replied that the form was filled. It hadn't called&lt;br&gt;
a single tool.&lt;/p&gt;

&lt;p&gt;The model wasn't making things up, exactly. It was reading. Its context showed&lt;br&gt;
&lt;code&gt;fill_form&lt;/code&gt; coming back ok moments earlier, in what it had every reason to&lt;br&gt;
think was its own chat, and it believed the record.&lt;/p&gt;

&lt;p&gt;The fix keys the list by chat, in&lt;br&gt;
&lt;a href="https://github.com/BrowyHQ/browy/blob/main/src/agent/recent-actions.ts" rel="noopener noreferrer"&gt;a small module of its own&lt;/a&gt;,&lt;br&gt;
and a new test fails if a fresh chat ever starts with another chat's tool&lt;br&gt;
calls again. Version 0.1.9 shipped the same day.&lt;/p&gt;

&lt;p&gt;The demo video came out of that same filming session. It runs 65 seconds in&lt;br&gt;
real time: one sentence typed into the side panel, a paper open in one tab,&lt;br&gt;
and a 26-field paper submission form in the other. By the end every field is&lt;br&gt;
filled, and the submit button is still waiting for me.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/arch-backend/" rel="noopener noreferrer"&gt;Architecture: native host&lt;/a&gt; How the host wraps the Copilot SDK and routes tool calls back to the extension.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
    <item>
      <title>AI browser agents compared: Browser Use, Skyvern, Nanobrowser, Browy</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Mon, 19 Oct 2026 19:58:25 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/ai-browser-agents-compared-browser-use-skyvern-nanobrowser-browy-491m</link>
      <guid>https://dev.to/ritabratamaiti/ai-browser-agents-compared-browser-use-skyvern-nanobrowser-browy-491m</guid>
      <description>&lt;p&gt;I built Browy, the first of the seven agents on this page.&lt;/p&gt;

&lt;p&gt;All seven can read a web page and click through it for you. The differences&lt;br&gt;
that matter are whose browser they drive and who pays for the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven browser agents side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Where it runs&lt;/th&gt;
&lt;th&gt;Whose browser and logins&lt;/th&gt;
&lt;th&gt;How you pay for the model&lt;/th&gt;
&lt;th&gt;Open source&lt;/th&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Browy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Help in the tabs you're signed into, next to DevTools&lt;/td&gt;
&lt;td&gt;Inside your browser, with a small local host&lt;/td&gt;
&lt;td&gt;Your own Chrome, Edge or Brave, with your sessions&lt;/td&gt;
&lt;td&gt;Your GitHub Copilot plan, Free included&lt;/td&gt;
&lt;td&gt;Yes, Apache-2.0&lt;/td&gt;
&lt;td&gt;Side panel chat and a DevTools panel REPL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Browser Use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agents you script and run at scale&lt;/td&gt;
&lt;td&gt;Your machine or Browser Use Cloud&lt;/td&gt;
&lt;td&gt;A fresh browser, your Chrome profile, or a cloud browser&lt;/td&gt;
&lt;td&gt;Your own API key, or Browser Use credits&lt;/td&gt;
&lt;td&gt;Yes, MIT&lt;/td&gt;
&lt;td&gt;Python library, CLI, cloud API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skyvern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Weekly jobs behind a login&lt;/td&gt;
&lt;td&gt;Your machine (pip or Docker) or Skyvern Cloud&lt;/td&gt;
&lt;td&gt;Its own browser, or your running Chrome&lt;/td&gt;
&lt;td&gt;Your own key when self-hosted; Cloud has free and paid plans&lt;/td&gt;
&lt;td&gt;Yes, AGPL-3.0&lt;/td&gt;
&lt;td&gt;Web app with a workflow builder, SDKs, API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nanobrowser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any model provider, local models included&lt;/td&gt;
&lt;td&gt;Inside your browser&lt;/td&gt;
&lt;td&gt;Your Chrome or Edge&lt;/td&gt;
&lt;td&gt;Your own API keys, or a local model through Ollama&lt;/td&gt;
&lt;td&gt;Yes, Apache-2.0&lt;/td&gt;
&lt;td&gt;Side panel chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stagehand&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser steps inside your own code&lt;/td&gt;
&lt;td&gt;Your machine or Browserbase&lt;/td&gt;
&lt;td&gt;A browser it launches or attaches to, or a cloud one&lt;/td&gt;
&lt;td&gt;Your own key, or their model gateway&lt;/td&gt;
&lt;td&gt;Yes, MIT&lt;/td&gt;
&lt;td&gt;SDK for Python, Go and TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude in Chrome&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude users, especially for scheduled tasks&lt;/td&gt;
&lt;td&gt;Inside Chrome&lt;/td&gt;
&lt;td&gt;Your Chrome&lt;/td&gt;
&lt;td&gt;A paid Claude plan&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Side panel, Claude Code, Claude Cowork&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handing off a task and walking away&lt;/td&gt;
&lt;td&gt;OpenAI's cloud, the desktop app, or Chrome&lt;/td&gt;
&lt;td&gt;A cloud browser, the app's browser, or your Chrome&lt;/td&gt;
&lt;td&gt;Your ChatGPT plan; the cloud browser needs a paid plan other than Go&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;ChatGPT on web, desktop and mobile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Browy, the agent I built for your signed-in browser
&lt;/h2&gt;

&lt;p&gt;Browy works in your own Chrome, Edge or Brave, in the tabs you already have&lt;br&gt;
open and the sessions you're already signed into. Ask it to review the pull&lt;br&gt;
request in front of you or set up a Gmail filter, and it starts from the page&lt;br&gt;
you're on, not from a login screen. It reads the page's accessibility tree and&lt;br&gt;
numbers each control, then clicks and types by number. When it fills a form,&lt;br&gt;
it stops before submitting unless you ask it to submit.&lt;/p&gt;

&lt;p&gt;It runs on the GitHub Copilot plan you already have, the same one your editor&lt;br&gt;
and terminal use. You sign in with GitHub, then choose from that plan's&lt;br&gt;
Claude, GPT and Gemini models and switch between them mid-chat with &lt;code&gt;/model&lt;/code&gt;&lt;br&gt;
or in Settings. Copilot Free works too.&lt;/p&gt;

&lt;p&gt;It's also the only agent on this page with its own panel inside Chrome&lt;br&gt;
DevTools. Open DevTools on any tab and the Browy panel gives you a REPL with&lt;br&gt;
slash commands and a JavaScript mode, next to the Network and Console tabs you&lt;br&gt;
already use. The agent can read that tab's network log and console itself, so&lt;br&gt;
you can ask which API call is slowest and get the URL, status and timing back.&lt;/p&gt;

&lt;p&gt;Browy is Apache-2.0, and the agent loop is one file you can read. Since&lt;br&gt;
v0.1.10, the tools that reach your machine rather than the page, from &lt;code&gt;bash&lt;/code&gt;&lt;br&gt;
to Browy's own &lt;code&gt;run_script&lt;/code&gt;, stay off until you switch them on in Settings.&lt;/p&gt;

&lt;p&gt;Getting started takes three steps: add the extension, run one install command,&lt;br&gt;
and sign in with GitHub. It suits developers who already pay for GitHub&lt;br&gt;
Copilot and want an agent in the tabs they're signed into, including the ones&lt;br&gt;
for sites they're building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Use, for agents you run from code
&lt;/h2&gt;

&lt;p&gt;Browser Use is the open-source project most people mean when they say&lt;br&gt;
"browser agent," and it earned that spot. Magnus Müller and Gregor Žunič&lt;br&gt;
built it as a Python library: you hand an &lt;code&gt;Agent&lt;/code&gt; a task and a model, it opens&lt;br&gt;
a browser, and it works through pages until it has an answer. It now comes in&lt;br&gt;
three forms: the MIT-licensed library, a command-line tool that gives a coding&lt;br&gt;
agent like Claude Code or Codex control of your browser, and Browser Use&lt;br&gt;
Cloud, which runs the agent and the browser for you.&lt;/p&gt;

&lt;p&gt;It suits developers who want an agent inside their own program, with custom&lt;br&gt;
tools and structured output, running whichever model they prefer: OpenAI,&lt;br&gt;
Anthropic, Browser Use's own BU2 model, or a local model through Ollama.&lt;/p&gt;

&lt;p&gt;Where it shines is work that runs while nobody is watching. The library runs&lt;br&gt;
inside your own program or on your own servers, and when you need many&lt;br&gt;
browsers at once, Browser Use Cloud sells them at $0.02 an hour with proxies&lt;br&gt;
and CAPTCHA handling built in. The library can also open your real Chrome&lt;br&gt;
profile with its logins (you may have to quit Chrome first).&lt;/p&gt;

&lt;p&gt;If you'd rather ask for help in the tabs you're already signed into than write&lt;br&gt;
a script, that's the job Browy is built for, and it runs on the Copilot plan&lt;br&gt;
you already pay for. Browser Use's code is on GitHub at&lt;br&gt;
&lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;browser-use/browser-use&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skyvern, for workflows you run every week
&lt;/h2&gt;

&lt;p&gt;Skyvern drives websites with language models and computer vision, so it can&lt;br&gt;
work through a site it has never seen. Its workflow builder chains steps into&lt;br&gt;
something you can rerun: log in, filter the invoices, loop over them, download&lt;br&gt;
each one. You can run it yourself with &lt;code&gt;pip&lt;/code&gt; or Docker and your own model key,&lt;br&gt;
or use Skyvern Cloud, which starts with a free tier of 5,000 credits and has&lt;br&gt;
paid plans from $29 a month. The code is AGPL-3.0, except for the anti-bot&lt;br&gt;
features, which live only in the cloud.&lt;/p&gt;

&lt;p&gt;It's built for teams with the same job to do behind a login, again and again.&lt;br&gt;
The examples in its README include invoice portals, job applications,&lt;br&gt;
government forms and insurance quotes.&lt;/p&gt;

&lt;p&gt;It shines at repetition. Skyvern handles 2FA codes, stores credentials, solves&lt;br&gt;
CAPTCHAs in its cloud and connects to Zapier, Make and n8n. It can also drive&lt;br&gt;
your own Chrome over remote debugging, with your cookies and logins.&lt;/p&gt;

&lt;p&gt;For one-off work in the tabs you're already signed into, Browy is the lighter&lt;br&gt;
fit, because you open it beside the page and ask. Skyvern's code is on GitHub&lt;br&gt;
at &lt;a href="https://github.com/Skyvern-AI/skyvern" rel="noopener noreferrer"&gt;Skyvern-AI/skyvern&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nanobrowser, the extension that takes any model
&lt;/h2&gt;

&lt;p&gt;Nanobrowser is the closest open-source relative of Browy. It's an Apache-2.0&lt;br&gt;
extension with a chat in the side panel, and it runs a pair of agents in your&lt;br&gt;
browser, a planner and a navigator, each of which can use a different model.&lt;br&gt;
Its README credits Browser Use as one of the projects it builds on. Version&lt;br&gt;
0.2.0 came out on October 2.&lt;/p&gt;

&lt;p&gt;It suits people who want an agent in the browser they already use, signed in&lt;br&gt;
where they're already signed in, and who'd rather choose the model provider&lt;br&gt;
themselves.&lt;/p&gt;

&lt;p&gt;Its strength is model choice. It works with OpenAI, Anthropic, Gemini,&lt;br&gt;
DeepSeek, Grok, OpenRouter and more, and with local models through Ollama, so&lt;br&gt;
a page's text never has to leave your machine. You add it from the Chrome Web&lt;br&gt;
Store, paste an API key into its settings and pick a model for each agent. It&lt;br&gt;
officially supports Chrome and Edge.&lt;/p&gt;

&lt;p&gt;If you already pay for GitHub Copilot, Browy gives you the same kind of side&lt;br&gt;
panel with that plan's models behind it, and adds a panel inside DevTools.&lt;br&gt;
Nanobrowser's code is on GitHub at&lt;br&gt;
&lt;a href="https://github.com/nanobrowser/nanobrowser" rel="noopener noreferrer"&gt;nanobrowser/nanobrowser&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stagehand, the SDK from Browserbase
&lt;/h2&gt;

&lt;p&gt;Stagehand is here because it's the main alternative to Browser Use when you're&lt;br&gt;
writing code rather than chatting. It's an MIT-licensed SDK from Browserbase&lt;br&gt;
for TypeScript, Python and Go, and it wraps a Playwright-style browser in a few&lt;br&gt;
AI calls. &lt;code&gt;act&lt;/code&gt; performs an action you describe in plain English, &lt;code&gt;observe&lt;/code&gt;&lt;br&gt;
finds elements and hands back real selectors, and &lt;code&gt;extract&lt;/code&gt; pulls data out&lt;br&gt;
against a schema you define. It can launch Chrome on your machine, attach to a&lt;br&gt;
Chromium browser you already have running, or use Browserbase's cloud&lt;br&gt;
browsers.&lt;/p&gt;

&lt;p&gt;It's a good fit for developers building a product or a pipeline that needs a&lt;br&gt;
browser, who want control over single steps rather than a whole agent loop.&lt;/p&gt;

&lt;p&gt;It's at its best as a building block. It lives in your codebase, so it gets&lt;br&gt;
tested and deployed like the rest of your code, and on Browserbase it can cache&lt;br&gt;
repeated actions. The login example in its README keeps passwords away from the&lt;br&gt;
model by filling fields through selectors. Browserbase has a free plan, and its&lt;br&gt;
paid plans start at $20 a month.&lt;/p&gt;

&lt;p&gt;If you want an agent to use rather than one to build, Browy works in the tabs&lt;br&gt;
you're signed into, on the Copilot plan you already pay for. Stagehand's code&lt;br&gt;
is on GitHub at&lt;br&gt;
&lt;a href="https://github.com/browserbase/stagehand" rel="noopener noreferrer"&gt;browserbase/stagehand&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude in Chrome, Anthropic's agent in your own Chrome
&lt;/h2&gt;

&lt;p&gt;Claude in Chrome is here because it's the closest thing to Browy from a model&lt;br&gt;
company: an extension that works in your own signed-in Chrome from a side&lt;br&gt;
panel. It reads the page you're on and works through it for you, forms&lt;br&gt;
included, and it also serves as the browser for Claude Code and Claude Cowork.&lt;br&gt;
It's available on every paid Claude plan, from Pro to Enterprise, and it&lt;br&gt;
supports desktop Chrome only.&lt;/p&gt;

&lt;p&gt;It suits people who already pay for Claude, and developers using Claude Code&lt;br&gt;
who want it to check its work in a real browser, console errors and network&lt;br&gt;
requests included.&lt;/p&gt;

&lt;p&gt;Where it shines is unattended work. It keeps working in the background while&lt;br&gt;
you use other tabs, and it can run tasks on a schedule, daily or weekly. Its&lt;br&gt;
classic side panel can also learn a workflow you record once. For control, you&lt;br&gt;
get site-by-site permissions and a safety check before it acts, and Team and&lt;br&gt;
Enterprise admins can switch it on or off for everyone.&lt;/p&gt;

&lt;p&gt;If your subscription is GitHub Copilot rather than Claude, or you work in Edge&lt;br&gt;
or Brave, Browy gives you the same kind of side panel in your own signed-in&lt;br&gt;
browser, with Claude among the models your Copilot plan offers. Anthropic's&lt;br&gt;
page for it is &lt;a href="https://claude.com/claude-in-chrome" rel="noopener noreferrer"&gt;Claude in Chrome&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT's cloud browser and Chrome extension, from OpenAI
&lt;/h2&gt;

&lt;p&gt;ChatGPT is here because OpenAI has retired its Atlas browser and moved browser&lt;br&gt;
work into ChatGPT itself, which is where a lot of people will try a browser&lt;br&gt;
agent first. On paid plans other than Go, ChatGPT Work can run a task in a&lt;br&gt;
cloud browser on a separate computer. That task keeps going after you leave&lt;br&gt;
the conversation, and it pauses when it needs you to sign in or confirm&lt;br&gt;
something. The ChatGPT desktop app for macOS and Windows has a browser of its&lt;br&gt;
own, and OpenAI's ChatGPT extension for Chrome lets the app work in your&lt;br&gt;
existing Chrome profile and signed-in sessions. The extension supports Chrome&lt;br&gt;
only and works together with the desktop app.&lt;/p&gt;

&lt;p&gt;It suits people who already live in ChatGPT, and anyone who wants to hand off&lt;br&gt;
a task and come back later to a result.&lt;/p&gt;

&lt;p&gt;It shines at delegation. You can start a cloud browser task from your phone,&lt;br&gt;
and it carries on without you, and you type passwords into a sign-in form that&lt;br&gt;
the model never sees.&lt;/p&gt;

&lt;p&gt;If you'd rather watch the work happen in your own tabs, next to DevTools,&lt;br&gt;
Browy is built for exactly that, and it runs on the Copilot plan you already&lt;br&gt;
pay for. OpenAI explains the cloud browser in&lt;br&gt;
&lt;a href="https://help.openai.com/en/articles/20001280-using-cloud-browser-in-chatgpt" rel="noopener noreferrer"&gt;Using cloud browser in ChatGPT&lt;/a&gt;,&lt;br&gt;
and the extension is&lt;br&gt;
&lt;a href="https://chromewebstore.google.com/detail/chatgpt/hehggadaopoacecdllhhajmbjkdcmajg" rel="noopener noreferrer"&gt;ChatGPT on the Chrome Web Store&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I'd pick
&lt;/h2&gt;

&lt;p&gt;If you already pay for GitHub Copilot and want an agent in the tabs you're&lt;br&gt;
signed into, start with Browy. That's who I built it for, and it's the only&lt;br&gt;
agent here that also lives inside DevTools.&lt;/p&gt;

&lt;p&gt;The others are built for different jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For agents that run unattended and at scale, from your own code, use Browser
Use, or Stagehand if you'd rather assemble smaller building blocks.&lt;/li&gt;
&lt;li&gt;For workflows behind a login that someone reruns every week, with 2FA and
stored credentials, look at Skyvern. If you pay for Claude, Claude in Chrome
can run tasks on a schedule.&lt;/li&gt;
&lt;li&gt;For local models, try Nanobrowser.&lt;/li&gt;
&lt;li&gt;To hand a task off and walk away, use ChatGPT's cloud browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's Browy filling a 26-field form from a paper in another tab, in real time:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;iframe
src="https://www.youtube.com/embed/bnellxgwJwY"
width="710"
height="399"
style="width: 100%; height: auto; aspect-ratio: 16 / 9;"
allowfullscreen
loading="lazy"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;/p&gt;One sentence, in real time: Browy reads the authors from a paper in another tab, fills all 26 fields, and stops before submit. &lt;a href="/demo/paper-form/"&gt;Try the same form.&lt;/a&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://browyhq.github.io/install/" rel="noopener noreferrer"&gt;install guide&lt;/a&gt; takes you from here to your first chat in about&lt;br&gt;
two minutes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>browser</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Your browser already knows who you are. The form does not.</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Tue, 13 Oct 2026 14:34:34 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/your-browser-already-knows-who-you-are-the-form-does-not-36c3</link>
      <guid>https://dev.to/ritabratamaiti/your-browser-already-knows-who-you-are-the-form-does-not-36c3</guid>
      <description>&lt;p&gt;The fifth journal portal of the year wants the author list again. Same eight&lt;br&gt;
names, same order, same affiliations, same ORCID identifiers, typed into eight&lt;br&gt;
separate pairs of first-name and last-name boxes because the form was designed&lt;br&gt;
in 2011 and has been load-bearing ever since. You have the data. It is in the&lt;br&gt;
last submission, which is in the portal you are looking at, three clicks away&lt;br&gt;
in a tab you are already signed into.&lt;/p&gt;

&lt;p&gt;This is the shape of most form work. Not one enormous form once, but the same&lt;br&gt;
medium-sized form over and over, on a site that knows exactly who you are, with&lt;br&gt;
data that already exists somewhere the browser can reach.&lt;/p&gt;

&lt;p&gt;It is also the thing browser agents are worst at, and the reason is structural.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two different problems wearing the same word
&lt;/h2&gt;

&lt;p&gt;"Automate this form" splits into two problems that have almost nothing in&lt;br&gt;
common.&lt;/p&gt;

&lt;p&gt;The first is filling a form on a public page. A signup, a contact form, a&lt;br&gt;
checkout. Anything can do this. Playwright has done it for years, headless&lt;br&gt;
browsers in the cloud do it at scale, and it works because the page is the same&lt;br&gt;
for everybody.&lt;/p&gt;

&lt;p&gt;The second is filling a form that only exists because you are signed in. A&lt;br&gt;
manuscript submission. An expense claim. A CRM record. A conference review. A&lt;br&gt;
grant portal. Here the page is assembled from your session: your saved drafts,&lt;br&gt;
your institution's custom fields, the co-authors you added last time, a&lt;br&gt;
half-finished record from three weeks ago.&lt;/p&gt;

&lt;p&gt;Almost every browser agent solves the first problem and quietly cannot touch&lt;br&gt;
the second. They launch a fresh browser, usually in a data centre. That browser&lt;br&gt;
has no cookies, no session, no extensions, and no history. Pointed at a portal,&lt;br&gt;
it lands on a login screen. You can hand it credentials, which means putting&lt;br&gt;
your institutional password into an agent's config, and then it meets your&lt;br&gt;
second factor and stops.&lt;/p&gt;

&lt;p&gt;The gap is not intelligence. A model that could reason its way through the form&lt;br&gt;
still never gets to see it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running inside the session you already have
&lt;/h2&gt;

&lt;p&gt;Browy takes the other route. It is a Chromium extension, so it drives the&lt;br&gt;
browser you are actually using: your profile, your cookies, your logged-in&lt;br&gt;
tabs, your password manager, your 2FA already satisfied an hour ago. There is&lt;br&gt;
no credential handoff because there is nothing to hand off. The page the agent&lt;br&gt;
sees is the page you see.&lt;/p&gt;

&lt;p&gt;That choice costs something, and it is worth naming. Browy cannot run a hundred&lt;br&gt;
of these overnight while you sleep. It is attached to a real browser on a real&lt;br&gt;
machine, so it is bounded by that. If you need volume against public pages,&lt;br&gt;
Browser Use or Skyvern will serve you better and I would point you at them.&lt;br&gt;
What this buys instead is access to the pages that only exist after login,&lt;br&gt;
which is where the repetitive work actually lives.&lt;/p&gt;
&lt;h2&gt;
  
  
  Indices, not selectors
&lt;/h2&gt;

&lt;p&gt;The second structural problem is how an agent refers to a field.&lt;/p&gt;

&lt;p&gt;The common approach is CSS selectors, either written by hand or generated by&lt;br&gt;
the model. Both are brittle in the same way. A selector like&lt;br&gt;
&lt;code&gt;div.form-group:nth-child(7) &amp;gt; input&lt;/code&gt; describes where something sat in the DOM&lt;br&gt;
at the moment it was written, and a React re-render or a conditionally shown&lt;br&gt;
field moves it. Stagehand's answer to this is self-healing selectors, which is&lt;br&gt;
a real approach to a real problem.&lt;/p&gt;

&lt;p&gt;Browy does something different. Before acting, it builds an indexed snapshot of&lt;br&gt;
the page from the accessibility tree, the same structure a screen reader&lt;br&gt;
consumes, and gives every interactive element a number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[12]&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author_1_given"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Given name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
[13]&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author_1_family"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Family name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
[14]&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author_1_role"&lt;/span&gt; &lt;span class="na"&gt;selected=&lt;/span&gt;&lt;span class="s"&gt;"Author"&lt;/span&gt; &lt;span class="na"&gt;options=&lt;/span&gt;&lt;span class="s"&gt;[Author,&lt;/span&gt; &lt;span class="na"&gt;Editor&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Reviewer&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
~[27]&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Add another author&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model works in those indices. &lt;code&gt;type_index(13, "Maiti")&lt;/code&gt; targets the element&lt;br&gt;
that snapshot resolved, not a path guessed from HTML the model half-remembers.&lt;br&gt;
Elements below the fold are marked with a tilde and scrolled into view&lt;br&gt;
automatically. Elements that appeared since the last snapshot are flagged, which&lt;br&gt;
matters more than it sounds: when a form reveals a conditional section, the new&lt;br&gt;
fields are the interesting ones and the agent is told which those are.&lt;/p&gt;

&lt;p&gt;The accessibility tree also carries the labels. Not &lt;code&gt;name="f_7"&lt;/code&gt; but "Given&lt;br&gt;
name", because that is what the browser exposes to assistive technology. A form&lt;br&gt;
that is accessible is, by accident, a form that is legible to an agent.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading the form before writing to it
&lt;/h2&gt;

&lt;p&gt;The part that changes the economics is doing the whole form in one pass.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;extract_form&lt;/code&gt; returns the structure: every field, its label, its type, whether&lt;br&gt;
it is required, its current value, and for a select, the options it will accept.&lt;br&gt;
That is one call and it produces something the model can plan against rather&lt;br&gt;
than discover field by field.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;fill_form&lt;/code&gt; takes an array and fills all of it in a single call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ritabrata"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maiti"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Author"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0000-0002-1825-0097"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One tool call, not twenty. This matters for a boring reason: every tool call is&lt;br&gt;
a round trip through the model, and a thirty-field form filled one field at a&lt;br&gt;
time is thirty round trips of latency and tokens, with thirty chances for the&lt;br&gt;
page to shift underneath. Reading the schema once and writing once collapses&lt;br&gt;
that.&lt;/p&gt;

&lt;p&gt;Type detection is handled per field, so checkboxes, radios and selects do not&lt;br&gt;
need separate calls. &lt;code&gt;submit_form&lt;/code&gt; is deliberately separate, and it reports&lt;br&gt;
whether the URL changed afterwards, because "did that actually submit" is a&lt;br&gt;
question worth answering rather than assuming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it still goes wrong
&lt;/h2&gt;

&lt;p&gt;Three places, honestly.&lt;/p&gt;

&lt;p&gt;Custom widgets that are not really form controls. A rich-text editor built on a&lt;br&gt;
contenteditable div, or a date picker that is fourteen nested divs and no&lt;br&gt;
&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, will not appear as a fillable field. The escape hatch is&lt;br&gt;
&lt;code&gt;evaluate_js&lt;/code&gt;, which runs in page context and can do anything the page can do,&lt;br&gt;
but at that point you are writing code rather than asking for something.&lt;/p&gt;

&lt;p&gt;Multi-step wizards where the next step depends on server validation of the&lt;br&gt;
current one. Browy handles these, but each step is a fresh snapshot and a fresh&lt;br&gt;
round trip, so a six-step wizard feels slow in a way a single page does not.&lt;/p&gt;

&lt;p&gt;And uploads still need a real file path. &lt;code&gt;upload_index&lt;/code&gt; bypasses the OS file&lt;br&gt;
dialog, which is the hard part, but you have to know where the file is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that made me build it
&lt;/h2&gt;

&lt;p&gt;The honest origin is that I was uploading a manuscript, for the fourth time&lt;br&gt;
that month, to a portal that had all of my previous submissions stored, and&lt;br&gt;
asking me to retype every author anyway. The data was sitting in the same&lt;br&gt;
browser session. It just had no way to move six inches across the screen.&lt;/p&gt;

&lt;p&gt;That is not a hard problem in the sense of requiring a better model. It is a&lt;br&gt;
plumbing problem: something that can read the page as structure rather than&lt;br&gt;
pixels, act on stable handles rather than guessed paths, and do it from inside&lt;br&gt;
the session where the data already lives.&lt;/p&gt;

&lt;p&gt;Browy is open source, Apache-2.0, and runs on a GitHub Copilot subscription you&lt;br&gt;
probably already have, so there is no second bill for the tokens. If you want to&lt;br&gt;
see the form tools specifically, they are in the&lt;br&gt;
&lt;a href="https://browyhq.github.io/tools/" rel="noopener noreferrer"&gt;tool reference&lt;/a&gt;, and&lt;br&gt;
&lt;a href="https://browyhq.github.io/install/" rel="noopener noreferrer"&gt;the install guide&lt;/a&gt; covers the two-part&lt;br&gt;
setup.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/tools/" rel="noopener noreferrer"&gt;Tool reference&lt;/a&gt; Every browser tool the agent can call, including extract_form and fill_form.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Every $20 AI subscription costs about $100 to serve. The bill is coming.</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:33:39 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/every-20-ai-subscription-costs-about-100-to-serve-the-bill-is-coming-7bb</link>
      <guid>https://dev.to/ritabratamaiti/every-20-ai-subscription-costs-about-100-to-serve-the-bill-is-coming-7bb</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/WPHYPeCjNv8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;A piece called&lt;br&gt;
&lt;a href="https://www.thestateofbrand.com/news/ai-subscription-time-bomb" rel="noopener noreferrer"&gt;AI subscriptions are a ticking time bomb for enterprise&lt;/a&gt;&lt;br&gt;
made the rounds this week, and the headline is the right shape of the&lt;br&gt;
story. Every major AI lab is running an industry-wide loss-leader at a&lt;br&gt;
scale that does not really have a precedent. Your company's $20 Claude&lt;br&gt;
Pro seats and $20 ChatGPT Plus seats are being served at something&lt;br&gt;
like five times the cost the lab is collecting for them, and that&lt;br&gt;
arrangement is not stable.&lt;/p&gt;

&lt;p&gt;The price tag has not moved in three years. The product has changed&lt;br&gt;
completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit economics
&lt;/h2&gt;

&lt;p&gt;A Claude Pro seat is $20 a month and gives you Sonnet 4.6, Opus 4.6,&lt;br&gt;
file creation, code execution, web search. On the API, those same&lt;br&gt;
models cost $3 input and $15 output per million tokens for Sonnet,&lt;br&gt;
$5 input and $25 output for Opus. A knowledge worker running Claude&lt;br&gt;
for a few hours a day, uploading documents, drafting reports, easily&lt;br&gt;
moves through enough tokens that the API-priced equivalent of that&lt;br&gt;
seat sits somewhere between $200 and $400 a month.&lt;/p&gt;

&lt;p&gt;Microsoft was reportedly losing more than $20 a month on every GitHub&lt;br&gt;
Copilot seat. Power users were hitting $80. One widely-cited analysis&lt;br&gt;
found Anthropic was burning about $8 of compute for every $1 of&lt;br&gt;
subscription revenue. The $20 sticker has been frozen since 2022 and&lt;br&gt;
the models in that window picked up image generation, code execution,&lt;br&gt;
voice, agentic reasoning, web search, and a generational capability&lt;br&gt;
jump. The number stayed put.&lt;/p&gt;

&lt;p&gt;That is the whole story. Everything from here is mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the subsidy was buying
&lt;/h2&gt;

&lt;p&gt;Cheap inference at consumer prices, deployed broadly to enterprises,&lt;br&gt;
was buying integration depth. The labs were not trying to make money&lt;br&gt;
on the seat. They were trying to make sure the seat existed in the&lt;br&gt;
first place, then make sure the company's marketing draft and the&lt;br&gt;
engineer's pull request review and the analyst's quarterly summary&lt;br&gt;
all happened through it. Once those workflows are load-bearing, the&lt;br&gt;
price can move. The dependency is the asset.&lt;/p&gt;

&lt;p&gt;You can see this in the language coming out of OpenAI. Nick Turley,&lt;br&gt;
their VP of product, described the subscription pricing as something&lt;br&gt;
they&lt;br&gt;
&lt;a href="https://danielmiessler.com/blog/ai-stops-being-artificially-cheap" rel="noopener noreferrer"&gt;stumbled into&lt;/a&gt;,&lt;br&gt;
and has floated phasing out unlimited plans entirely, comparing them&lt;br&gt;
to "unlimited electricity." Sam Altman said publicly that OpenAI now&lt;br&gt;
needs to become "an AI inference company," which is the polite version&lt;br&gt;
of admitting the consumer subscription was a customer-acquisition line&lt;br&gt;
item, not a P&amp;amp;L.&lt;/p&gt;

&lt;p&gt;The KPMG Q1 2026 pulse has U.S. organizations projecting average AI&lt;br&gt;
spending of $207 million over the next twelve months, roughly double&lt;br&gt;
the year before. A Goldman Sachs survey of large companies has most&lt;br&gt;
of them overrunning their AI budgets by orders of magnitude.&lt;br&gt;
Chandrasekaran, who runs AI and data at KPMG North America, told&lt;br&gt;
&lt;a href="https://www.marketplace.org/story/2026/04/24/how-much-is-too-much-to-spend-on-ai-tools" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt;&lt;br&gt;
the quiet part: "Even a quarter or two ago nobody bothered about LLM&lt;br&gt;
consumption costs." It is the bother stage now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents are what broke the math
&lt;/h2&gt;

&lt;p&gt;The reason the subsidy held as long as it did is that AI was a chatbot.&lt;br&gt;
You typed, it answered, you read the answer, repeat. A normal session&lt;br&gt;
was a few thousand tokens. Heavy use ran into the tens of thousands.&lt;br&gt;
At those volumes, $20 a seat was uncomfortable for the lab but not&lt;br&gt;
catastrophic.&lt;/p&gt;

&lt;p&gt;Agents do not look like that.&lt;/p&gt;

&lt;p&gt;A Claude Code session runs autonomously for an extended period. It&lt;br&gt;
reads files, writes files, runs commands, looks at the output, decides&lt;br&gt;
what to do next, repeats. Users have been&lt;br&gt;
&lt;a href="https://www.uncoveralpha.com/p/the-era-of-subsidized-ai-model-usage" rel="noopener noreferrer"&gt;exhausting five-hour rate-limit windows in under ninety minutes&lt;/a&gt;.&lt;br&gt;
Multiple agents in parallel on a single project multiply that. A&lt;br&gt;
developer running three or four concurrent coding agents is consuming&lt;br&gt;
something close to an order of magnitude more tokens than the same&lt;br&gt;
person in chat, and the subscription price on the seat is unchanged.&lt;/p&gt;

&lt;p&gt;GitHub took the obvious next step. On June 1, 2026, Copilot&lt;br&gt;
&lt;a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/" rel="noopener noreferrer"&gt;moves to usage-based billing&lt;/a&gt;,&lt;br&gt;
specifically because the flat-fee model collapsed under agentic&lt;br&gt;
workloads. The announcement spelled it out: agentic usage is becoming&lt;br&gt;
the default, the inference demand is qualitatively different, the&lt;br&gt;
pricing has to follow.&lt;/p&gt;

&lt;p&gt;Everyone else will do the same thing on a delay.&lt;/p&gt;

&lt;h2&gt;
  
  
  The enterprise position
&lt;/h2&gt;

&lt;p&gt;This is where it gets ugly for organizations that have not done the&lt;br&gt;
work.&lt;/p&gt;

&lt;p&gt;Over the past two years, thousands of companies have woven $20 AI&lt;br&gt;
subscriptions deep into operations. Marketing drafts copy through&lt;br&gt;
ChatGPT Plus. Engineering writes and reviews code through Claude Pro.&lt;br&gt;
Research synthesizes documents. Customer success summarizes tickets.&lt;br&gt;
Finance models scenarios. The line items are budgeted at subsidized&lt;br&gt;
prices because that is what the bill currently says. The actual cost&lt;br&gt;
of the same workloads at API rates, if the lab were charging it, is&lt;br&gt;
fifteen to twenty times higher.&lt;/p&gt;

&lt;p&gt;When prices adjust, two things happen at once. The bill goes up, and&lt;br&gt;
the workflows are already too embedded to rip out. The subsidy creates&lt;br&gt;
the dependency, the dependency makes the price increase unavoidable.&lt;br&gt;
That is the trap, and there is no clever way out of it.&lt;/p&gt;

&lt;p&gt;The companies that survive this transition cleanly will be the ones&lt;br&gt;
that did the bookkeeping. Track per-team token consumption. Know which&lt;br&gt;
workflows are genuinely high-value and which are running Claude on&lt;br&gt;
something a 2018 script could have done. Have a sense of which&lt;br&gt;
subscriptions can move to per-seat billing if it has to, and which&lt;br&gt;
ones become structurally expensive overnight.&lt;/p&gt;

&lt;p&gt;The companies that don't survive cleanly will discover the bill in&lt;br&gt;
the third week of whichever month the subsidy ends, with no time to&lt;br&gt;
re-budget and no leverage to renegotiate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it means for agent tools
&lt;/h2&gt;

&lt;p&gt;The shake-out hits the agent tool market harder than it hits the labs.&lt;br&gt;
The labs are losing on the seat, but they own the inference. They can&lt;br&gt;
move the price. They can change the plan. They can introduce&lt;br&gt;
usage-based tiers and call them "for power users." The seat is still&lt;br&gt;
there at the end.&lt;/p&gt;

&lt;p&gt;The agent tool, the wrapper, the IDE plugin, the browser extension&lt;br&gt;
that uses the lab's API or subscription on your behalf, is in a worse&lt;br&gt;
position. If it has its own per-seat subscription, it is selling you&lt;br&gt;
something whose underlying cost just doubled, and it has to either eat&lt;br&gt;
that or pass it on. If it bills on its own meter on top of the lab's,&lt;br&gt;
it is asking enterprises to swallow a usage line item that already had&lt;br&gt;
no budget code.&lt;/p&gt;

&lt;p&gt;The agent tools that survive the next twelve months will be the ones&lt;br&gt;
that don't sell their own meter. Tools that piggyback on the&lt;br&gt;
subscription the user already has. Tools that don't introduce a second&lt;br&gt;
billing surface for finance to police. Tools whose cost curve is&lt;br&gt;
shaped by the lab's pricing, not by the tool's overhead.&lt;/p&gt;

&lt;p&gt;This is not a clever positioning argument. It is what happens to every&lt;br&gt;
software-on-top-of-software market when the underlying utility starts&lt;br&gt;
charging real prices. The tools that own the price stack survive. The&lt;br&gt;
tools that resell the utility at a markup get squeezed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on what I work on
&lt;/h2&gt;

&lt;p&gt;I build &lt;a href="https://browyhq.github.io/" rel="noopener noreferrer"&gt;Browy&lt;/a&gt;, an open-source AI agent that lives&lt;br&gt;
in a Chrome side panel and a DevTools REPL. It drives the real browser&lt;br&gt;
tabs you have open. The thing it does not have is its own subscription.&lt;br&gt;
It uses your existing&lt;br&gt;
&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; subscription for&lt;br&gt;
the model. The model call goes from your machine to GitHub, the answer&lt;br&gt;
comes back, the rest happens locally.&lt;/p&gt;

&lt;p&gt;When Copilot moves to usage-based billing on June 1, you pay GitHub&lt;br&gt;
the new rate, the same as you would have anyway. Browy doesn't sit&lt;br&gt;
between you and that bill. It doesn't add a per-seat charge of its&lt;br&gt;
own. It doesn't run a metered tier on top. That is not a special&lt;br&gt;
business decision on our end, it is the only decision that survives&lt;br&gt;
the shake-out described above. The tools that try to live on top of a&lt;br&gt;
collapsing subsidy by adding their own subsidy get squeezed twice.&lt;/p&gt;

&lt;p&gt;That is most of the story I wanted to put down. The original piece is&lt;br&gt;
&lt;a href="https://www.thestateofbrand.com/news/ai-subscription-time-bomb" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
The 30-second video version is at the top of this post.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/ai-slop-killed-the-bug-bounty/" rel="noopener noreferrer"&gt;AI slop killed the open-source bug bounty&lt;/a&gt; Earlier essay on the other end of the same arc: cheap inference, not yet expensive.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aisubscriptions</category>
      <category>githubcopilot</category>
      <category>claudepro</category>
      <category>chatgptplus</category>
    </item>
    <item>
      <title>Google quietly declared war on the open web.</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:32:53 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/google-quietly-declared-war-on-the-open-web-43bd</link>
      <guid>https://dev.to/ritabratamaiti/google-quietly-declared-war-on-the-open-web-43bd</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/cxqv5HIIFVo" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;At Google I/O this week, the Search team made AI Overviews the default&lt;br&gt;
answer surface for English users. The&lt;br&gt;
&lt;a href="https://blog.google/products-and-platforms/products/search/search-io-2026/" rel="noopener noreferrer"&gt;official keynote post&lt;/a&gt;&lt;br&gt;
buries the change under a lot of "agentic" and "personal intelligence"&lt;br&gt;
language, but the practical effect is small and definite: Search now&lt;br&gt;
answers from a Google-hosted synthesis. The blue links are a fallback,&lt;br&gt;
not the product.&lt;/p&gt;

&lt;p&gt;This was the inevitable next step of a series of changes that started&lt;br&gt;
with featured snippets and accelerated with AI Overviews. The&lt;br&gt;
&lt;a href="https://www.techrepublic.com/article/google-ai-overviews-inaccurate-answers-analysis/" rel="noopener noreferrer"&gt;10 percent inaccuracy rate&lt;/a&gt;&lt;br&gt;
on AI Overviews is now everyone's problem, because Overviews are&lt;br&gt;
everyone's default answer.&lt;/p&gt;

&lt;p&gt;The&lt;br&gt;
&lt;a href="https://tante.cc/2026/05/20/on-google-declaring-war-on-the-web/" rel="noopener noreferrer"&gt;piece on tante.cc&lt;/a&gt;&lt;br&gt;
called this "declaring war on the web," and the wording is exactly&lt;br&gt;
right. What follows is what I think the change actually is, the deal&lt;br&gt;
that broke, and what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deal that built the web
&lt;/h2&gt;

&lt;p&gt;Sites let Google crawl them. Google sent traffic back. Both sides&lt;br&gt;
won. The crawler got fresh content; the site got readers, and the&lt;br&gt;
readers had a clear path back to the source when they wanted to dig&lt;br&gt;
deeper.&lt;/p&gt;

&lt;p&gt;This contract held for about twenty years. It built the open web as&lt;br&gt;
a publishing surface, the indie blog as a thing that paid rent, the&lt;br&gt;
SEO industry, the long-tail of niche reference sites, and most of&lt;br&gt;
what we now call "documentation on the internet." It was an&lt;br&gt;
asymmetric trade, but both sides understood it. You give Google the&lt;br&gt;
catalog. Google sends you the foot traffic.&lt;/p&gt;

&lt;p&gt;AI Overviews quietly cancelled the trade.&lt;/p&gt;

&lt;p&gt;The crawler still runs. The catalog still gets ingested. The output&lt;br&gt;
on the user's screen is now a Google-synthesised answer, with the&lt;br&gt;
sources demoted to a row of tiny chip-shaped citations the median&lt;br&gt;
user does not click. The reader gets an answer, often correct,&lt;br&gt;
sometimes wrong, and never has to leave the search results page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is, named plainly
&lt;/h2&gt;

&lt;p&gt;The framing in the original piece is sharp and worth quoting at&lt;br&gt;
length:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your work, your writing or art do matter a bit still: As (unpaid)&lt;br&gt;
raw material for their synthetic text extruders. You get to work&lt;br&gt;
for free so Google can have tight control on the flow of&lt;br&gt;
information and make sure that the responses people get are in&lt;br&gt;
line with what they need them to be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the right frame. The web is not the product anymore. The&lt;br&gt;
web is the raw material. The product is the synthesised view on top&lt;br&gt;
of it, hosted on Google's infrastructure, branded with Google's UI,&lt;br&gt;
and accountable to Google's editorial choices.&lt;/p&gt;

&lt;p&gt;A useful analogy: the relationship between a wire service and a&lt;br&gt;
newspaper. The wire produces the underlying reporting; the newspaper&lt;br&gt;
picks, edits, and presents. The reader sees the newspaper. For a&lt;br&gt;
long time the web was the newspaper. Google was the index, the table&lt;br&gt;
of contents that pointed at the newspaper. AI Overviews flipped that.&lt;br&gt;
Google is the newspaper now. Your site is the wire.&lt;/p&gt;

&lt;p&gt;The thing that makes this disorienting is that nobody negotiated the&lt;br&gt;
new arrangement. The publishers did not agree to be wire&lt;br&gt;
contributors. The wire contributors did not get paid. The newspaper&lt;br&gt;
masthead never asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is bigger than search-traffic math
&lt;/h2&gt;

&lt;p&gt;The temptation is to treat this as an SEO problem. Traffic numbers&lt;br&gt;
will drop, sites will adapt, the market will sort it out. That misses&lt;br&gt;
the scale.&lt;/p&gt;

&lt;p&gt;Google holds the largest single browser deployment on the planet,&lt;br&gt;
the largest single mobile OS, the largest single web video platform,&lt;br&gt;
the dominant share of web standards committee participation, the&lt;br&gt;
dominant share of ad-tech, and now the dominant search-answer&lt;br&gt;
surface. A single company sets the abstraction layer at every point&lt;br&gt;
between the publisher and the reader. When that company decides the&lt;br&gt;
abstraction layer should not include outbound links, there is no&lt;br&gt;
counter-party large enough to disagree at scale.&lt;/p&gt;

&lt;p&gt;This is the part the SEO frame misses. The market does not have a&lt;br&gt;
way to sort out a question of this scale because the market does not&lt;br&gt;
have a participant of this scale. The closest analogue is regulatory.&lt;br&gt;
The EU has begun acting. The DOJ case is grinding along. Neither is&lt;br&gt;
fast enough to matter at the speed Google can ship.&lt;/p&gt;

&lt;p&gt;The web is going to survive this. The independent publisher, the&lt;br&gt;
solo blog, the niche reference site, the corner of the internet&lt;br&gt;
written by one human for fifteen others, will get squeezed in the&lt;br&gt;
process. Some will respond by exiting. Some will paywall. Some will&lt;br&gt;
move to Substack or to mailing lists, which is just running their own&lt;br&gt;
distribution stack. The shape of the open web will change a lot,&lt;br&gt;
faster than the SEO market expects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do as a user
&lt;/h2&gt;

&lt;p&gt;The advice older than the problem still applies. Run your own tools.&lt;br&gt;
Pick your own sources. Don't outsource your reading to a system whose&lt;br&gt;
incentives are to keep you on its surface.&lt;/p&gt;

&lt;p&gt;Concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use a search engine that links out.&lt;/strong&gt; Kagi, Marginalia, Mojeek,
Searx, DuckDuckGo, Brave Search. None are perfect, all are linking
out by default. The web still indexes; the index is just not the
default surface anymore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep an RSS reader.&lt;/strong&gt; Feedbin, Inoreader, NetNewsWire on the Mac,
Readwise Reader if you want web archiving. Subscribing directly to
the publishers you trust routes you around the answer-surface
entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a personal allowlist of go-to sources.&lt;/strong&gt; The handful of
sites you check first when you want a real answer. For me that's
about thirty sites; the search engine is where I go when none of
them have it. This is roughly how everyone used the web in 2005.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read source material when the stakes are high.&lt;/strong&gt; AI Overviews are
wrong 10 percent of the time. That rate is fine for "what is the
recipe for pancakes." It is not fine for "what does this medication
interact with."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is the same in each case: keep the human in the loop&lt;br&gt;
when the cost of wrong is high, and let the machine condense when the&lt;br&gt;
cost of wrong is low.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this has to do with what I build
&lt;/h2&gt;

&lt;p&gt;I work on &lt;a href="https://browyhq.github.io/" rel="noopener noreferrer"&gt;Browy&lt;/a&gt;, an open-source AI agent that&lt;br&gt;
lives in a Chromium extension. It drives the real browser tab you&lt;br&gt;
have open. You ask it a question; it inspects the page, follows&lt;br&gt;
links, reads other tabs, and writes you an answer with the sources&lt;br&gt;
visible inline. It does not synthesise from a hosted index. It&lt;br&gt;
operates on the web you would see if you were doing the work yourself.&lt;/p&gt;

&lt;p&gt;That is not a coincidence. It is the response to exactly the&lt;br&gt;
disintermediation move described above. If the answer-surface is&lt;br&gt;
hosted by a counter-party whose incentives are not yours, the answer&lt;br&gt;
you want is the one you can produce yourself, against the sources&lt;br&gt;
you trust, on the page that loaded in front of you.&lt;/p&gt;

&lt;p&gt;The web is not ending. The middleman is trying to swallow it. Run&lt;br&gt;
your own tools.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>google</category>
      <category>search</category>
      <category>aioverviews</category>
      <category>aisearch</category>
    </item>
    <item>
      <title>GitHub got pwned through one VSCode extension. 3,800 repos.</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:32:12 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/github-got-pwned-through-one-vscode-extension-3800-repos-hgj</link>
      <guid>https://dev.to/ritabratamaiti/github-got-pwned-through-one-vscode-extension-3800-repos-hgj</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/CcBjlKit9IY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;GitHub confirmed this week that roughly 3,800 of its internal&lt;br&gt;
repositories were exfiltrated after one of its own employees installed&lt;br&gt;
a trojanised version of the Nx Console extension for VSCode. The&lt;br&gt;
malicious extension came through the official VS Code Marketplace, ran&lt;br&gt;
with the editor's permissions, and quietly handed back the contents of&lt;br&gt;
the employee's repo cache. GitHub has since linked the campaign to the&lt;br&gt;
&lt;a href="https://www.bleepingcomputer.com/news/security/github-links-repo-breach-to-tanstack-npm-supply-chain-attack/" rel="noopener noreferrer"&gt;TanStack npm supply chain attack&lt;/a&gt;&lt;br&gt;
from earlier this month and removed the poisoned extension from the&lt;br&gt;
marketplace.&lt;/p&gt;

&lt;p&gt;The TeamPCP group is asking $50,000 for the dump. As far as anyone&lt;br&gt;
outside GitHub can tell, no customer data has moved.&lt;/p&gt;

&lt;p&gt;That is the news. The interesting part is the design that made it&lt;br&gt;
possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The editor has no sandbox
&lt;/h2&gt;

&lt;p&gt;A VSCode extension runs as Node.js code inside the editor's process.&lt;br&gt;
It has the editor's permissions. That means it can read every file in&lt;br&gt;
every workspace you have ever opened, every token in your&lt;br&gt;
&lt;code&gt;~/.aws/credentials&lt;/code&gt;, every entry in your SSH agent, and every byte&lt;br&gt;
that ever passed through &lt;code&gt;git push&lt;/code&gt;. The Marketplace itself has no&lt;br&gt;
sandbox model. There has been&lt;br&gt;
&lt;a href="https://github.com/microsoft/vscode/issues/52116" rel="noopener noreferrer"&gt;a feature request open since 2018&lt;/a&gt;&lt;br&gt;
asking Microsoft to ship one. The request is still open.&lt;/p&gt;

&lt;p&gt;This is not a VSCode-specific complaint. It is the model. Cursor&lt;br&gt;
inherits the architecture. Windsurf inherits it. JetBrains plugins&lt;br&gt;
run with the IDE's permissions, just packaged differently. Vim and&lt;br&gt;
Emacs have had this problem since plugins were invented; the only&lt;br&gt;
reason it never blew up at this scale is that nobody put a marketplace&lt;br&gt;
in front of them.&lt;/p&gt;

&lt;p&gt;The Marketplace is the part that scales the blast radius. Twelve&lt;br&gt;
months ago, a set of malicious extensions disguised as legitimate&lt;br&gt;
tools racked up&lt;br&gt;
&lt;a href="https://www.bleepingcomputer.com/news/security/vscode-extensions-with-9-million-installs-pulled-over-security-risks/" rel="noopener noreferrer"&gt;9 million installs&lt;/a&gt;&lt;br&gt;
before takedown. A different set posed as&lt;br&gt;
&lt;a href="https://www.bleepingcomputer.com/news/security/malicious-vscode-extensions-infect-windows-with-cryptominers/" rel="noopener noreferrer"&gt;cryptominer-laden helpers&lt;/a&gt;.&lt;br&gt;
A WhiteCobra-flooded batch of twenty-four extensions, one of them with&lt;br&gt;
&lt;a href="https://www.bleepingcomputer.com/news/security/ai-slop-ransomware-test-sneaks-on-to-vs-code-marketplace/" rel="noopener noreferrer"&gt;basic ransomware capability&lt;/a&gt;,&lt;br&gt;
made it onto the marketplace before anyone noticed. And in January, a&lt;br&gt;
pair of malicious AI-coding-assistant extensions with&lt;br&gt;
&lt;a href="https://www.bleepingcomputer.com/news/security/malicious-ai-extensions-on-vscode-marketplace-steal-developer-data/" rel="noopener noreferrer"&gt;1.5 million installs&lt;/a&gt;&lt;br&gt;
silently shipped developer data to servers in China.&lt;/p&gt;

&lt;p&gt;Add it up. Across the visible-to-the-public campaigns, this is more&lt;br&gt;
than 11 million installs of code that should not have been there. The&lt;br&gt;
GitHub breach is one well-placed install away from "any of the above&lt;br&gt;
got an SRE."&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug, named honestly
&lt;/h2&gt;

&lt;p&gt;The threat model in this story is not a clever attacker. The threat&lt;br&gt;
model is a checkbox the operator did not read carefully because they&lt;br&gt;
were trying to get work done.&lt;/p&gt;

&lt;p&gt;A developer at GitHub had an editor open. The editor said: install&lt;br&gt;
this extension. It looked like Nx Console; the developer needed Nx&lt;br&gt;
Console. They clicked install. Three thousand eight hundred internal&lt;br&gt;
repos went out the back door. There is no version of "be more&lt;br&gt;
careful" that fixes this. The marketplace is the install surface and&lt;br&gt;
the marketplace cannot, in its current shape, distinguish the real&lt;br&gt;
extension from the malicious one with the same name.&lt;/p&gt;

&lt;p&gt;The fix is at the layer the operator doesn't have to think about.&lt;br&gt;
That is either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The editor ships a sandbox. Extensions ask for the file paths,
network endpoints, and OS capabilities they need. The user reviews
a manifest, the same way they review extension permissions in a
browser. Microsoft has been promising this since 2018.&lt;/li&gt;
&lt;li&gt;The marketplace adds an integrity layer that an attacker cannot
forge. Reproducible builds, source mirroring, signed releases.
Nothing on the marketplace today provides this end to end.&lt;/li&gt;
&lt;li&gt;The user installs nothing they have not personally read or that
does not come from a vendor whose source they can audit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Option 3 is the only one available today. Options 1 and 2 require&lt;br&gt;
Microsoft to ship work that has been promised for seven years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for browser extensions
&lt;/h2&gt;

&lt;p&gt;I work on a browser extension, so I want to be clear about what does&lt;br&gt;
and does not transfer from this story.&lt;/p&gt;

&lt;p&gt;Chromium MV3 extensions are not VSCode extensions. The permission&lt;br&gt;
model is real. An extension declares the URLs it can touch&lt;br&gt;
(&lt;code&gt;host_permissions&lt;/code&gt;) and the capabilities it can use (&lt;code&gt;debugger&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;storage&lt;/code&gt;, &lt;code&gt;nativeMessaging&lt;/code&gt;, and so on) in &lt;code&gt;manifest.json&lt;/code&gt;. The&lt;br&gt;
browser enforces those declarations. An extension that says&lt;br&gt;
&lt;code&gt;"host_permissions": ["https://gmail.com/*"]&lt;/code&gt; literally cannot reach&lt;br&gt;
&lt;code&gt;bank.example.com&lt;/code&gt;. The Chrome Web Store review process is not&lt;br&gt;
perfect, but it does look at the manifest before publish.&lt;/p&gt;

&lt;p&gt;That makes the browser-extension surface meaningfully better than the&lt;br&gt;
editor-extension surface for the same class of attack. It does not&lt;br&gt;
make it safe. Things to watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;/code&gt;&lt;/strong&gt; is a real permission Chrome will grant if the user
agrees. Many general-purpose extensions ask for it. Browy asks for
it (we have to; you point us at any tab you visit). What stops
abuse is the next thing on the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CRX you download is signed.&lt;/strong&gt; It is not necessarily the
source you read. Chrome Web Store does not require reproducible
builds. If the vendor publishes obfuscated code, you are trusting
the vendor, not the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native messaging is the escape hatch.&lt;/strong&gt; Browy has a native host
that runs on your machine. So does any "remote control" extension,
any password manager native bridge, and many "AI assistants." A
malicious extension can ship a clean source repo and a malicious
native binary, and the user has no easy way to know.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list of things that would help Chrome users defend themselves is&lt;br&gt;
roughly the same as for VSCode users: a sandbox the user reviews, a&lt;br&gt;
marketplace integrity layer the attacker cannot forge, and a habit of&lt;br&gt;
installing nothing whose source you have not read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do about this in Browy
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://browyhq.github.io/" rel="noopener noreferrer"&gt;Browy&lt;/a&gt; is open-source under Apache-2.0. The&lt;br&gt;
source ships in the release. The&lt;br&gt;
&lt;a href="https://github.com/BrowyHQ/browy/blob/main/src/agent/loop.ts" rel="noopener noreferrer"&gt;agent loop is in one file&lt;/a&gt;&lt;br&gt;
and the&lt;br&gt;
&lt;a href="https://github.com/BrowyHQ/browy/blob/main/src/agent/tools/browser.ts" rel="noopener noreferrer"&gt;tool registry is in another&lt;/a&gt;.&lt;br&gt;
A reviewer who reads either file in twenty minutes has read the&lt;br&gt;
attack surface. There is no Browy server. The model call goes from&lt;br&gt;
your machine to GitHub Copilot via the local Copilot CLI; everything&lt;br&gt;
else is local.&lt;/p&gt;

&lt;p&gt;The host-touching tools (&lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;read_file&lt;/code&gt;, &lt;code&gt;write_file&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;glob&lt;/code&gt;, &lt;code&gt;web_fetch&lt;/code&gt;) are off by default. To use any of them you have&lt;br&gt;
to open Settings, find the tool, and turn it on. The browser-driving&lt;br&gt;
tools are on by default but each can be turned off the same way. We&lt;br&gt;
ship a debugger banner whenever a session is live, because Chrome&lt;br&gt;
makes that mandatory and we are happy with that.&lt;/p&gt;

&lt;p&gt;None of this prevents a future me from going rogue and shipping a&lt;br&gt;
malicious release. It does mean that anyone who cares can pin a&lt;br&gt;
known-good build, audit any future build before installing, and run&lt;br&gt;
their own from source. The fix for the marketplace problem is not a&lt;br&gt;
better marketplace. It is a habit of looking at what you install.&lt;/p&gt;

&lt;p&gt;The short version: when the editor has no sandbox, the only sandbox&lt;br&gt;
left is your attention. Spend it before you click install, not after.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/ai-slop-killed-the-bug-bounty/" rel="noopener noreferrer"&gt;AI slop killed the open-source bug bounty&lt;/a&gt; The other end of the same arc: cheap inference, asymmetric inbox, maintainer attention as the limiting factor.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>vscode</category>
      <category>security</category>
      <category>supplychain</category>
    </item>
    <item>
      <title>AI slop killed the open-source bug bounty</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:31:31 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/ai-slop-killed-the-open-source-bug-bounty-2o64</link>
      <guid>https://dev.to/ritabratamaiti/ai-slop-killed-the-open-source-bug-bounty-2o64</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/8ml0p8BM5Eo" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Turso, the SQLite-compatible database written in Rust, retired its bug&lt;br&gt;
bounty program this week. The post explaining why is titled, dryly,&lt;br&gt;
&lt;a href="https://turso.tech/blog/the-wonders-of-ai" rel="noopener noreferrer"&gt;The wonders of AI&lt;/a&gt;. For&lt;br&gt;
about a year the project had been paying $1,000 for each critical&lt;br&gt;
vulnerability someone reported in the codebase. Budget wasn't the&lt;br&gt;
problem. The problem was that most of the reports landing in the&lt;br&gt;
maintainer's inbox were written by an LLM, and the maintainer was&lt;br&gt;
spending most of his bounty hours reading text that no human had really&lt;br&gt;
intended for him to read.&lt;/p&gt;

&lt;p&gt;The examples are funny in a bleak way. One submission claimed to have&lt;br&gt;
found a critical flaw that let an attacker execute arbitrary SQL&lt;br&gt;
statements. Against a SQL database. Another report on the same project&lt;br&gt;
described a buffer overflow whose reproduction steps included editing&lt;br&gt;
Turso's own source code, recompiling with a forced volatile write past&lt;br&gt;
the end of a vector, and running the modified binary. The "vulnerable&lt;br&gt;
code paths" did not exist. The "exploits" did not reproduce.&lt;/p&gt;

&lt;p&gt;So the maintainers ended the program. That is a small story about one&lt;br&gt;
database, and also a very large story about every queue on the internet&lt;br&gt;
that involves humans on one side and an open submission form on the&lt;br&gt;
other. Every maintainer who has read Turso's post this week has&lt;br&gt;
recognised their own inbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contract that worked
&lt;/h2&gt;

&lt;p&gt;The economics of a bug bounty are tidy. The project says: if you find a&lt;br&gt;
real problem that meets this severity bar, we pay you $X. Both sides&lt;br&gt;
win. The hunter gets paid for genuinely useful work. The project gets&lt;br&gt;
a security audit it could not have afforded any other way.&lt;/p&gt;

&lt;p&gt;That contract rested on a quiet assumption. The cost of submitting a&lt;br&gt;
report was supposed to be in the same neighbourhood as the cost of&lt;br&gt;
producing one. Producing a real exploit is hard. Writing one up takes&lt;br&gt;
hours. So submission volume self-throttled. The queue stayed roughly&lt;br&gt;
the size of the real-findings rate, which is the rate a small team can&lt;br&gt;
actually triage.&lt;/p&gt;

&lt;p&gt;Cheap inference broke that assumption in about eighteen months.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;It now costs roughly a tenth of a cent to ask a frontier model to&lt;br&gt;
produce a vulnerability report against a public codebase. The model&lt;br&gt;
will produce one. The report will look real enough that a human has to&lt;br&gt;
spend about ten minutes reading it before concluding that it isn't.&lt;br&gt;
Submission is effectively free. Triage costs the most expensive thing&lt;br&gt;
a software project has, which is the unbroken attention of the one&lt;br&gt;
person who understands the code.&lt;/p&gt;

&lt;p&gt;You don't need many people running that arbitrage to break a small&lt;br&gt;
program. Once the submission rate climbs past the triage rate, the&lt;br&gt;
queue does not stabilise. It grows until the maintainer either gives&lt;br&gt;
up reading or gives up the program. Turso gave up the program.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same shape, everywhere
&lt;/h2&gt;

&lt;p&gt;The bug bounty story is just one shape of a pattern that is now visible&lt;br&gt;
in a dozen places.&lt;/p&gt;

&lt;p&gt;Drive-by pull requests are flooding popular GitHub repos. Most are&lt;br&gt;
LLM-generated typo fixes or hallucinated refactors. Maintainers either&lt;br&gt;
auto-close everything and lose the legitimate ones, or burn out on&lt;br&gt;
triage. Open issue trackers are seeing the same flood of "I think there&lt;br&gt;
might be a bug" reports with no reproduction, often filed by a chatbot&lt;br&gt;
middle layer that promised a confused user it would "let the developers&lt;br&gt;
know." Code review comments are now arriving thirty at a time on small&lt;br&gt;
PRs, mostly tautologies and hedges. The comment threads on technical&lt;br&gt;
blog posts are heading the same way.&lt;/p&gt;

&lt;p&gt;The shape is always the same. Producing the input costs nothing.&lt;br&gt;
Processing the input still costs a person.&lt;/p&gt;

&lt;p&gt;We have automated finding bugs. We have automated submitting bugs. This&lt;br&gt;
year we are automating rejecting bugs. Nobody is automating fixing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottleneck was never the writing
&lt;/h2&gt;

&lt;p&gt;A lot of the early excitement about LLMs in software was that they&lt;br&gt;
could write code very fast. That turned out to be true, and it turned&lt;br&gt;
out to be the less interesting half of the problem. Writing the code&lt;br&gt;
was never the bottleneck. Reading the code is the bottleneck. Reviewing&lt;br&gt;
the code, understanding the code, deciding whether to merge the code,&lt;br&gt;
deciding whether to ship the code, deciding what to do when the code&lt;br&gt;
breaks at 2am. The reading-and-deciding loop is where engineering&lt;br&gt;
actually happens, and it has always been bounded by how many things a&lt;br&gt;
human can hold in their head at once.&lt;/p&gt;

&lt;p&gt;LLMs add to the writing side of that loop and add to the input side of&lt;br&gt;
every queue feeding into it. They don't move the reading-and-deciding&lt;br&gt;
bottleneck. They make it the limiting factor for almost everything.&lt;/p&gt;

&lt;p&gt;This is why "just put an AI in the triage loop" doesn't fix the Turso&lt;br&gt;
problem. If there's a model deciding which reports a human sees,&lt;br&gt;
you've created a new game: produce reports that pass the model. That&lt;br&gt;
game is also cheap to play. The judgment step that the attacker is&lt;br&gt;
trying to commodify is the exact step that has to stay with a person,&lt;br&gt;
because that's what was being attacked in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What might actually hold
&lt;/h2&gt;

&lt;p&gt;A handful of patterns are starting to circulate in maintainer&lt;br&gt;
conversations. None are clean.&lt;/p&gt;

&lt;p&gt;A refundable submission deposit is the most-discussed option. You put&lt;br&gt;
down a small amount, say $20. You get it back if the report holds up.&lt;br&gt;
You lose it if it's slop. The economics work. The downside is that it&lt;br&gt;
filters out the hobbyist newcomer who doesn't have a card on file,&lt;br&gt;
which is exactly the population the open programs were designed to&lt;br&gt;
find.&lt;/p&gt;

&lt;p&gt;Reputation gating goes the other way. First-time submitters have to&lt;br&gt;
clear a higher bar, including a full reproduction and a vouching&lt;br&gt;
introduction. Established hunters skip the bar. This rebuilds an&lt;br&gt;
apprenticeship model that hasn't really existed in software security&lt;br&gt;
for a generation, but it raises the cost of discovering new talent.&lt;/p&gt;

&lt;p&gt;A few projects have started running honeypots. The clearest example is&lt;br&gt;
&lt;a href="https://github.com/UnsafeLabs/Bounty-Hunters" rel="noopener noreferrer"&gt;UnsafeLabs/Bounty-Hunters&lt;/a&gt;,&lt;br&gt;
which publishes a bounty-shaped repo that exists mainly to attract&lt;br&gt;
automated scanners. The submissions feed a public&lt;br&gt;
&lt;a href="https://clankers-leaderboard.pages.dev" rel="noopener noreferrer"&gt;leaderboard of automated submitters&lt;/a&gt;.&lt;br&gt;
You can read it as petty, or you can read it as the first attempt at&lt;br&gt;
something a search-engine spam team would recognise: a shared blocklist&lt;br&gt;
for the actor side of an asymmetric inbox problem.&lt;/p&gt;

&lt;p&gt;Some teams are moving programs off public surfaces entirely and onto&lt;br&gt;
verified-identity platforms like HackerOne or Bugcrowd, where the&lt;br&gt;
platform handles attribution. Small projects can't usually afford the&lt;br&gt;
platform fees. Bigger ones lose the discovery effect of an open&lt;br&gt;
program.&lt;/p&gt;

&lt;p&gt;The most interesting idea I've seen circulating among maintainers is&lt;br&gt;
what someone called proof of code. Before you can submit a bounty&lt;br&gt;
report, you have to land a small, accepted PR somewhere in the&lt;br&gt;
project. The PR doesn't have to be security-related. It just has to be&lt;br&gt;
real engineering by a person who understood the surrounding code. That&lt;br&gt;
converts a cheap text-generation game into a more expensive engineering&lt;br&gt;
game. It doesn't solve the problem. It tilts the asymmetry the other&lt;br&gt;
way.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is bigger than bounties
&lt;/h2&gt;

&lt;p&gt;The same dynamic is coming for everything that depends on an open&lt;br&gt;
queue with humans on the receiving end. Customer support tickets.&lt;br&gt;
Conference paper submissions. Open peer review. Public consultation&lt;br&gt;
periods on regulation. Government FOIA requests. Hiring inboxes. The&lt;br&gt;
comment thread under this post, probably.&lt;/p&gt;

&lt;p&gt;The economic concept is older than the internet. It is the tragedy of&lt;br&gt;
the commons applied to attention instead of grazing land. What's new&lt;br&gt;
is the rate at which the cost of producing input is falling. Most of&lt;br&gt;
the institutions that grew up around the old cost curve don't have&lt;br&gt;
time to redesign before the next drop.&lt;/p&gt;

&lt;p&gt;The next decade of public-facing system design is going to be about&lt;br&gt;
adding friction back in on purpose. Identity. Deposits. Reputation.&lt;br&gt;
Proof of work. Vouching. We spent twenty years stripping friction out&lt;br&gt;
of every form on the internet because friction was the enemy of&lt;br&gt;
growth. Now friction is the only thing protecting the people on the&lt;br&gt;
receiving end of those forms. The companies that figure out how to add&lt;br&gt;
it without losing the magic are going to eat everyone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on what "AI agent" means
&lt;/h2&gt;

&lt;p&gt;There are two things people now mean by "AI agent," and they are&lt;br&gt;
nearly opposite.&lt;/p&gt;

&lt;p&gt;One is the slop pipeline that broke Turso. Cheap inference pointed at&lt;br&gt;
any open queue, with no human in the loop, generating volume that&lt;br&gt;
other humans then have to filter. The person whose name is on the&lt;br&gt;
submission usually isn't watching what's submitted. Often they don't&lt;br&gt;
even know what was submitted.&lt;/p&gt;

&lt;p&gt;The other is the tool a person uses to do their actual work. An agent&lt;br&gt;
that drives the browser tab they're sitting in front of, that reads&lt;br&gt;
the codebase they're working on, that runs the command they would&lt;br&gt;
have typed. The person is at the keyboard. They see every action.&lt;br&gt;
Nothing gets sent to anyone else's queue without them watching it&lt;br&gt;
happen.&lt;/p&gt;

&lt;p&gt;These two things share a phrase and almost nothing else. The first one&lt;br&gt;
is a tax on every public surface on the internet. The second one is a&lt;br&gt;
power tool. The fact that they have the same name in the press is&lt;br&gt;
going to cost the second category a lot of goodwill before things&lt;br&gt;
sort themselves out.&lt;/p&gt;

&lt;p&gt;I work on the second kind. &lt;a href="https://browyhq.github.io/" rel="noopener noreferrer"&gt;Browy&lt;/a&gt; is an&lt;br&gt;
open-source AI agent that lives in a Chrome side panel and a DevTools&lt;br&gt;
REPL. It drives your real browser tabs through chat. There is no Browy&lt;br&gt;
server in the loop. There is no inbox you can flood by talking to it.&lt;br&gt;
There is a person, you, watching every click and every form fill it&lt;br&gt;
makes. Whatever happens to the public commons over the next few years,&lt;br&gt;
the part of the internet that's a tool you operate yourself is still&lt;br&gt;
going to be a good place to live.&lt;/p&gt;

&lt;p&gt;The 30-second video version of this argument is at the top of this&lt;br&gt;
post. Turso's own write-up is&lt;br&gt;
&lt;a href="https://turso.tech/blog/the-wonders-of-ai" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/mullvad-vpn-fingerprint/" rel="noopener noreferrer"&gt;Mullvad gave you 8 trillion exit IPs. 9 servers found you.&lt;/a&gt; Same researcher-pattern thinking, different domain: a VPN whose deterministic IP picker leaks identity across servers.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aislop</category>
      <category>bugbounty</category>
      <category>turso</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Mullvad gave you 8 trillion exit IPs. 9 servers found you.</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:30:05 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/mullvad-gave-you-8-trillion-exit-ips-9-servers-found-you-4md8</link>
      <guid>https://dev.to/ritabratamaiti/mullvad-gave-you-8-trillion-exit-ips-9-servers-found-you-4md8</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/g-B_toki7PA" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Mullvad is one of the few VPN providers that hands you a different public&lt;br&gt;
IP for each of its servers. With 578 servers in the fleet, a naive&lt;br&gt;
calculation gives you over 8 trillion possible combinations of exit IPs&lt;br&gt;
across the network. The pitch is that you melt into a very large crowd.&lt;/p&gt;

&lt;p&gt;A researcher who writes as tmctmt sat down with a script, rotated through&lt;br&gt;
3,650 WireGuard keys, and watched which exit IPs each key was assigned&lt;br&gt;
across nine servers. They expected to see thousands of distinct&lt;br&gt;
combinations. They saw 284. The&lt;br&gt;
&lt;a href="https://tmctmt.com/posts/mullvad-exit-ips-as-a-fingerprinting-vector/" rel="noopener noreferrer"&gt;full write-up is here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The cause is unglamorous and the impact is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the picker works
&lt;/h2&gt;

&lt;p&gt;When you connect to a Mullvad server, the exit IP you get is not random.&lt;br&gt;
It is picked deterministically from a per-server pool, seeded by your&lt;br&gt;
WireGuard public key. The pool sizes vary by server. Sydney exposes 60&lt;br&gt;
IPs, Helsinki 66, Los Angeles 91, Santiago 11. The key rotates every&lt;br&gt;
one to thirty days if you use the official app, and never if you bring&lt;br&gt;
your own WireGuard client.&lt;/p&gt;

&lt;p&gt;So far so reasonable. Per-key stickiness avoids hammering a single&lt;br&gt;
external IP with users who reconnect every few minutes, and it lets&lt;br&gt;
sites that ratelimit by IP behave sanely for a Mullvad user inside one&lt;br&gt;
session.&lt;/p&gt;

&lt;p&gt;The problem is the picker itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  One float, many pools
&lt;/h2&gt;

&lt;p&gt;The picker seeds a standard library RNG with the pubkey, then draws a&lt;br&gt;
single floating point number in the range zero to one. It scales that&lt;br&gt;
float to the size of the current server's pool to produce an index.&lt;br&gt;
That part is plausible. The part that breaks privacy is that the float&lt;br&gt;
is the same float on every server, because the RNG is seeded with the&lt;br&gt;
same pubkey and only the upper bound changes between calls.&lt;/p&gt;

&lt;p&gt;The consequence is that your exit IPs across servers land in the same&lt;br&gt;
percentile of each pool. If your float happens to be 0.82, you sit at&lt;br&gt;
position 49 of 60 in Sydney, position 9 of 11 in Santiago, position 54&lt;br&gt;
of 66 in Helsinki. Different IPs, same relative slot.&lt;/p&gt;

&lt;p&gt;If you have ever wondered why two of the smaller Mullvad servers seem&lt;br&gt;
to give you the "same" index, this is why. Santiago and Johannesburg&lt;br&gt;
both have pools of 11. With the same float scaled to the same size,&lt;br&gt;
they hand you the same position.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 8.2 trillion collapsed to 284
&lt;/h2&gt;

&lt;p&gt;This is the headline of the post. The pool sizes multiply to 8.2 trillion&lt;br&gt;
on paper. In practice the float is one number, so the entire vector of&lt;br&gt;
exit IPs across all servers is parameterised by that one number. The&lt;br&gt;
researcher's 3,650 sampled pubkeys produced only 284 distinct&lt;br&gt;
combinations across nine servers because the space being sampled is&lt;br&gt;
not thirteen-dimensional. It is one-dimensional, then projected.&lt;/p&gt;

&lt;p&gt;The 284 figure is the resolution of that projection at nine servers.&lt;br&gt;
With more servers the resolution rises, because each additional pool&lt;br&gt;
size carves the unit interval into finer buckets. With fewer servers&lt;br&gt;
it drops, but it does not drop as much as you would hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  What that means for a user
&lt;/h2&gt;

&lt;p&gt;The author built a small tool that takes a set of observed exit IPs and&lt;br&gt;
back-solves for the float interval that produced them. Nine well-chosen&lt;br&gt;
exit IPs squeeze the interval down to roughly 0.0034 wide. At an&lt;br&gt;
estimated 100,000 active Mullvad users that is around 340 people.&lt;/p&gt;

&lt;p&gt;Three hundred and forty is a lot of people in a crowd. It is not a lot&lt;br&gt;
of people in a deanonymisation attack. If a forum has IP logs and&lt;br&gt;
suspects two accounts are the same user, and the two accounts both&lt;br&gt;
connected through Mullvad on different servers, the overlap of the&lt;br&gt;
float intervals derived from those exits gives a very high probability&lt;br&gt;
that the accounts share a pubkey. Pair that with timing, user agent,&lt;br&gt;
language headers, or the kind of low-effort browser fingerprint that&lt;br&gt;
ad networks already collect, and the pool of suspects shrinks to one.&lt;/p&gt;

&lt;p&gt;The interesting attack is not a moderator with one forum's logs. It is&lt;br&gt;
the joining of logs across services that already happens in litigation,&lt;br&gt;
in subpoenas, and in stolen breach data. Mullvad's per-server IP&lt;br&gt;
diversity stops being a defence the moment two unrelated services&lt;br&gt;
contribute their exit IP records to the same correlation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The response
&lt;/h2&gt;

&lt;p&gt;Mullvad's co-founder posted a response on the morning the article went&lt;br&gt;
up. The short version: some of the described behaviour was intentional,&lt;br&gt;
some was not, the unintended part is being patched on a subset of the&lt;br&gt;
fleet first, and the intended part is now under internal review. He&lt;br&gt;
also asked, gently, that future researchers consider notifying the&lt;br&gt;
vendor before publishing.&lt;/p&gt;

&lt;p&gt;That is about the best response a small vendor can give to a Tuesday&lt;br&gt;
morning surprise, and it points at the part of this story that is most&lt;br&gt;
worth sitting with.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug under the bug
&lt;/h2&gt;

&lt;p&gt;The proximate bug is a seeded RNG whose first draw was treated as a&lt;br&gt;
fresh random number per call, when it is the same number every call.&lt;br&gt;
A defensible code review could miss this. The Rust documentation does&lt;br&gt;
not put the behaviour on a billboard, and the test that would have&lt;br&gt;
caught it, "do my exit IP percentiles correlate across pools," is not&lt;br&gt;
the test anyone writes.&lt;/p&gt;

&lt;p&gt;The deeper bug is the design assumption that per-key stickiness is&lt;br&gt;
compatible with per-server independence. It is not, for any picker&lt;br&gt;
that derives its index from a single seeded float. To get true&lt;br&gt;
independence you have to either reseed per call with a per-server salt,&lt;br&gt;
or pre-mix the pubkey with the server identity before drawing, or&lt;br&gt;
abandon determinism and accept the operational cost.&lt;/p&gt;

&lt;p&gt;There is a clean lesson here about cryptographic determinism that I&lt;br&gt;
will not labour, but the practical one for anyone shipping&lt;br&gt;
privacy-flavoured software is worth saying out loud. Deterministic&lt;br&gt;
behaviour is the thing that makes user experience predictable and the&lt;br&gt;
thing that makes users linkable across observations. The instinct to&lt;br&gt;
make a system feel coherent across sessions is the same instinct that&lt;br&gt;
hands an analyst a join key.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a user can do today
&lt;/h2&gt;

&lt;p&gt;The author lists two mitigations. Both reduce the value of the&lt;br&gt;
fingerprint without removing it.&lt;/p&gt;

&lt;p&gt;The first is to avoid switching servers while your pubkey is unchanged.&lt;br&gt;
If you only ever connect from one Mullvad server, an observer sees one&lt;br&gt;
exit IP and gets one of the pool's possible float intervals, not the&lt;br&gt;
intersection of many. That interval covers a much larger share of the&lt;br&gt;
user base.&lt;/p&gt;

&lt;p&gt;The second is to force-rotate your pubkey, which the Mullvad app will&lt;br&gt;
do if you log out and log back in. A new pubkey produces a new float&lt;br&gt;
and a new vector of exit IPs. Past observations of you, before the&lt;br&gt;
rotation, still link to whatever activity happened under the old key.&lt;/p&gt;

&lt;p&gt;Neither of these is satisfying. Both are what the user can do until&lt;br&gt;
the patch lands.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger picture for privacy tools
&lt;/h2&gt;

&lt;p&gt;VPNs are sold as a tool for blending in. The category has spent ten&lt;br&gt;
years competing on server count, jurisdiction, and "we don't log."&lt;br&gt;
None of that helps if the tunnel itself emits a stable signal that&lt;br&gt;
correlates across exits. Mullvad is one of the better-regarded&lt;br&gt;
operators in the category, run by people who care about this stuff&lt;br&gt;
and respond like it. If this design slipped past them, it is worth&lt;br&gt;
assuming similar designs are sitting unexamined in operators who&lt;br&gt;
care less.&lt;/p&gt;

&lt;p&gt;The healthier framing for anyone using a privacy tool is that the tool&lt;br&gt;
moves the boundary of who can see you. It does not make you invisible&lt;br&gt;
to the people inside the new boundary. A VPN protects you from your&lt;br&gt;
ISP and from the cafe Wi-Fi. It does not protect you from the website&lt;br&gt;
you are visiting, which still sees your browser, your timing, and&lt;br&gt;
whatever your tunnel happens to leak about your identity across&lt;br&gt;
sessions. Pretending otherwise is how people end up surprised.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this has to do with browsers
&lt;/h2&gt;

&lt;p&gt;A lot of the leak surface in modern web sessions is in the browser,&lt;br&gt;
not in the tunnel. Canvas fingerprinting, font enumeration, audio&lt;br&gt;
context fingerprinting, third party cookies that you thought were&lt;br&gt;
blocked, redirect chains that smuggle identifiers in the URL, the&lt;br&gt;
clipboard, the way your tabs talk to each other, the way an extension&lt;br&gt;
you forgot you installed talks to its origin. The interesting thing&lt;br&gt;
about the Mullvad story is that it is one of the rare cases where the&lt;br&gt;
leak is below the browser, and the browser would not see it without&lt;br&gt;
help.&lt;/p&gt;

&lt;p&gt;I work on &lt;a href="https://browyhq.github.io/" rel="noopener noreferrer"&gt;Browy&lt;/a&gt;, an open-source AI agent that&lt;br&gt;
lives in a Chrome side panel and a DevTools REPL. It drives the real&lt;br&gt;
tab you are looking at. You can point it at a page and ask, in&lt;br&gt;
English, what the page is sending, what it stores, what it loads from&lt;br&gt;
where, what it is trying to identify you with. It will open the&lt;br&gt;
Network tab and the Application tab and read them for you, and it&lt;br&gt;
will tell you what it found in the same chat where you asked the&lt;br&gt;
question. It does not send your browsing anywhere. The model call&lt;br&gt;
goes out and the answer comes back, the rest happens on your machine.&lt;/p&gt;

&lt;p&gt;The Mullvad bug is a server-side problem. Browy will not see it. But&lt;br&gt;
the broader habit, watching what your browser actually emits while&lt;br&gt;
you are using it, is what every category of privacy leak in the last&lt;br&gt;
decade has rewarded. The 30-second video version of the Mullvad story&lt;br&gt;
is at the top of this post. The full research, with the seed&lt;br&gt;
estimator tool, is&lt;br&gt;
&lt;a href="https://tmctmt.com/posts/mullvad-exit-ips-as-a-fingerprinting-vector/" rel="noopener noreferrer"&gt;at tmctmt's site&lt;/a&gt;.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/" rel="noopener noreferrer"&gt;All posts&lt;/a&gt; Index of every post on the Browy blog.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://browyhq.github.io/blog/ai-slop-killed-the-bug-bounty/" rel="noopener noreferrer"&gt;AI slop killed the open-source bug bounty&lt;/a&gt; The same shape in software security: a real database project just retired its bounty program because AI submissions broke the queue.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mullvad</category>
      <category>vpn</category>
      <category>fingerprinting</category>
      <category>privacy</category>
    </item>
    <item>
      <title>AnyModal: Train Multimodal LLMs in PyTorch</title>
      <dc:creator>Ritabrata Maiti</dc:creator>
      <pubDate>Tue, 19 Nov 2024 11:13:42 +0000</pubDate>
      <link>https://dev.to/ritabratamaiti/anymodal-simplify-multimodal-ai-development-with-a-flexible-framework-4o12</link>
      <guid>https://dev.to/ritabratamaiti/anymodal-simplify-multimodal-ai-development-with-a-flexible-framework-4o12</guid>
      <description>&lt;p&gt;Today, I want to introduce an open-source framework I’ve been working on: &lt;strong&gt;&lt;a href="https://github.com/ritabratamaiti/AnyModal" rel="noopener noreferrer"&gt;AnyModal&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;During my work on machine learning projects, I struggled to find flexible solutions for training multimodal LLMs. While there are plenty of great tools for specific tasks—like image classification or audio processing—there was no straightforward way to combine these modalities with large language models (LLMs). The process was often tedious, involving boilerplate code, custom integration, and a lot of trial and error to make different components work together.&lt;/p&gt;

&lt;p&gt;This frustration led me to build &lt;strong&gt;AnyModal&lt;/strong&gt;, a framework designed to reduce the complexity of multimodal AI development. It provides a modular, reusable structure that makes it easier for developers and researchers to combine diverse data types and experiment with new ideas without reinventing the wheel every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AnyModal&lt;/strong&gt; is built with the following objectives in mind:  &lt;/p&gt;

&lt;h4&gt;
  
  
  Reduce Boilerplate Code
&lt;/h4&gt;

&lt;p&gt;Combining modalities like images or audio with LLMs typically involves repetitive steps—preprocessing, encoding, tokenizing, and integrating. AnyModal minimizes this boilerplate by providing reusable modules for common tasks, letting developers focus on building smarter systems faster.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Enable Seamless Integration
&lt;/h4&gt;

&lt;p&gt;Whether you're working with images using a Vision Transformer (ViT) or audio spectrograms, AnyModal offers plug-and-play components that simplify the integration process. This makes it easy to handle multiple data types within a single framework.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Encourage Experimentation and Customization
&lt;/h4&gt;

&lt;p&gt;AnyModal supports rapid prototyping while offering the flexibility to customize components like feature encoders, projection layers, and tokenizers. It’s versatile enough for both quick experiments and production-level deployments.  &lt;/p&gt;




&lt;h3&gt;
  
  
  Example Usage: Integrating Images with LLMs
&lt;/h3&gt;

&lt;p&gt;Here’s a detailed example of how AnyModal simplifies the integration of image data into LLMs:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Install Dependencies
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;torch transformers datasets torchvision tqdm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Initialize Vision Components
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ViTImageProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ViTForImageClassification&lt;/span&gt;

&lt;span class="c1"&gt;# Load a pre-trained Vision Transformer
&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ViTImageProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;google/vit-base-patch16-224&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vision_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ViTForImageClassification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;google/vit-base-patch16-224&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define a Vision Encoder to extract feature embeddings
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;vision&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VisionEncoder&lt;/span&gt;
&lt;span class="n"&gt;vision_encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VisionEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vision_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Initialize Tokenizer and LLM
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;

&lt;span class="c1"&gt;# Load a pre-trained LLM and its tokenizer
&lt;/span&gt;&lt;span class="n"&gt;llm_tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;llm_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Define a Projection Layer
&lt;/h4&gt;



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

&lt;span class="c1"&gt;# Create a projection layer to map vision embeddings to LLM token space
&lt;/span&gt;&lt;span class="n"&gt;vision_tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Projector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;in_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vision_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;out_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Combine Everything with AnyModal
&lt;/h4&gt;



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

&lt;span class="c1"&gt;# Build the multimodal model
&lt;/span&gt;&lt;span class="n"&gt;multimodal_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MultiModalModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;input_processor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_encoder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vision_encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vision_tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language_tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_start_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|imstart|&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_end_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;|imend|&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;prompt_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Describe this image: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Training and Inference
&lt;/h4&gt;

&lt;p&gt;Training involves processing batches of image-text pairs and optimizing the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;torch.utils.data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DataLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;

&lt;span class="c1"&gt;# Load a sample dataset
&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image_caption_dataset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Prepare DataLoader
&lt;/span&gt;&lt;span class="n"&gt;train_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Training Loop
&lt;/span&gt;&lt;span class="n"&gt;optimizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AdamW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multimodal_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3e-4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;train_loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;multimodal_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Generate captions
&lt;/span&gt;&lt;span class="n"&gt;sample_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;generated_caption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multimodal_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generated Caption:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generated_caption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Current Status
&lt;/h3&gt;

&lt;p&gt;AnyModal is currently in its early stages, with the latest version supporting tasks like:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LaTeX OCR
&lt;/li&gt;
&lt;li&gt;Chest X-Ray Captioning (in progress)
&lt;/li&gt;
&lt;li&gt;Image Captioning
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Future planned features include support for visual question answering and audio captioning.  &lt;/p&gt;

&lt;p&gt;As the framework evolves, I’m focusing on expanding its functionality, refining the codebase, and addressing community feedback to move towards a stable release.  &lt;/p&gt;




&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ritabratamaiti/AnyModal" rel="noopener noreferrer"&gt;https://github.com/ritabratamaiti/AnyModal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reddit&lt;/strong&gt;: &lt;a href="https://www.reddit.com/r/AnyModal/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/AnyModal/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face&lt;/strong&gt;: &lt;a href="https://huggingface.co/AnyModal" rel="noopener noreferrer"&gt;https://huggingface.co/AnyModal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you’re looking for a way to simplify multimodal AI development, give &lt;strong&gt;AnyModal&lt;/strong&gt; a try. I’d love to hear your feedback or ideas for new features. Contributions are always welcome!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>llm</category>
      <category>ai</category>
      <category>multimodal</category>
    </item>
  </channel>
</rss>
