<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[h5i]]></title><description><![CDATA[h5i]]></description><link>https://koukyosyumei.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>h5i</title><link>https://koukyosyumei.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 10 Sep 2026 03:15:47 GMT</lastBuildDate><atom:link href="https://koukyosyumei.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[h5i: a headless browser purely written in Rust. no Chromium, no V8]]></title><description><![CDATA[What changes when a browser is designed for models instead of humans?
Imagine fifty AI agents opening documentation pages at the same time.
None of them needs a toolbar. None cares about smooth scroll]]></description><link>https://koukyosyumei.hashnode.dev/h5i-a-headless-browser-purely-written-in-rust-no-chromium-no-v8</link><guid isPermaLink="true">https://koukyosyumei.hashnode.dev/h5i-a-headless-browser-purely-written-in-rust-no-chromium-no-v8</guid><category><![CDATA[claude-code]]></category><category><![CDATA[codex]]></category><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[Security]]></category><category><![CDATA[browser]]></category><dc:creator><![CDATA[Hideaki Takahashi]]></dc:creator><pubDate>Thu, 03 Sep 2026 23:55:17 GMT</pubDate><content:encoded><![CDATA[<p><em>What changes when a browser is designed for models instead of humans?</em></p>
<p>Imagine fifty AI agents opening documentation pages at the same time.</p>
<p>None of them needs a toolbar. None cares about smooth scrolling. Most will not watch a video, use WebGL, or install an extension. They need the text, a few links and form controls, and enough state to decide what to do next.</p>
<p>Yet the usual solution is to start fifty copies of a browser designed for people.</p>
<p>This is not a criticism of Chromium. Its compatibility is extraordinary, and Playwright makes it one of the most practical automation targets available. But it made me wonder: if the workload is different, should the browser be different too?</p>
<p>That question led me to build <a href="https://github.com/h5i-dev/h5i">h5i</a>, an open-source headless browser for AI agents. The browser engine runs as a single Rust process. It does not launch Chromium, embed V8, or communicate through Chrome DevTools Protocol.</p>
<p>This post is about the design decisions behind it, what became possible after removing the human-browser assumptions, and where the approach still falls short.</p>
<h2>The browser can be the booleneck in an agent loop</h2>
<p>A browser session for an agent often looks like this:</p>
<ol>
<li><p>Open a page.</p>
</li>
<li><p>Convert the page into something a model can understand.</p>
</li>
<li><p>Let the model choose an action.</p>
</li>
<li><p>Perform the action.</p>
</li>
<li><p>Send the resulting page back to the model.</p>
</li>
</ol>
<p>When Chromium sits underneath this loop, several layers come along for the ride. The browser maintains renderer processes and a complete web platform. An automation client talks to it through a protocol. Another layer turns the DOM or accessibility tree into model input. Request interception and audit logging may be attached separately.</p>
<p>That architecture is justified when the target is the full modern web, that only humans care about. It is less obviously necessary when the task is “read this documentation page and follow the installation link.”</p>
<p>I wanted a much more optimized browser for three things that become important when browser sessions are created by agents rather than people:</p>
<ul>
<li><p>A small amount of state per session</p>
</li>
<li><p>An interface that does not waste model context</p>
</li>
<li><p>Network policy that is part of the browser, not an optional observer</p>
</li>
</ul>
<p>These constraints shaped almost every implementation choice.</p>
<h2>Building the engine from Rust components</h2>
<p>h5i is not a browser wrapper. Its engine combines several Rust projects with a custom networking layer:</p>
<table>
<thead>
<tr>
<th>Browser responsibility</th>
<th>Component</th>
</tr>
</thead>
<tbody><tr>
<td>HTML and DOM</td>
<td><a href="https://github.com/DioxusLabs/blitz">Blitz</a></td>
</tr>
<tr>
<td>CSS</td>
<td>Stylo</td>
</tr>
<tr>
<td>JavaScript</td>
<td><a href="https://github.com/boa-dev/boa">Boa</a></td>
</tr>
<tr>
<td>Software rendering</td>
<td><code>vello_cpu</code></td>
</tr>
<tr>
<td>Requests, policy, and recording</td>
<td>h5i</td>
</tr>
</tbody></table>
<p>Together, these components support navigation, forms, cookies, JavaScript, screenshots, WebSockets, Server-Sent Events, and structured extraction.</p>
<p>The interesting part is not that each component happens to be written in Rust. It is that h5i owns the path from an agent action to the outgoing network request. There is no remote-controlled browser process in the middle.</p>
<p>That gives the project a much smaller scope, but it also forces an honest tradeoff. Boa does not behave like V8, and h5i does not implement the entire browser platform. Content-heavy pages and ordinary interactions are the primary target. A complex React application, nested iframes, DRM-protected media, or WebGL workload may require Chromium.</p>
<p>The reduced compatibility is not an accidental limitation hidden behind the word “lightweight.” It is the cost of choosing a smaller browser.</p>
<h2>The page is a state machine</h2>
<p>The first interface I wanted to reconsider was page observation.</p>
<p>A screenshot is natural for a person, but expensive and ambiguous for a model. A raw DOM contains more implementation detail than the agent usually needs. Even a useful accessibility tree can become large when it is serialized after every action.</p>
<p>h5i instead produces a compact snapshot with references attached to actionable elements:</p>
<pre><code class="language-bash">h5i browser open https://docs.rs/ --allow docs.rs
h5i browser snapshot
</code></pre>
<p>The agent might see a link identified as <code>@e3</code>. It can act on the reference directly:</p>
<pre><code class="language-bash">h5i browser click @e3
</code></pre>
<p>The reference belongs to the snapshot that introduced it. If navigation or JavaScript changes the page and the old identifier is no longer safe, h5i rejects the action as stale. This is preferable to silently clicking whatever happens to match an old selector.</p>
<p>The next observation can be a delta:</p>
<pre><code class="language-bash">h5i browser snapshot --delta
</code></pre>
<p>Instead of serializing the page again, h5i reports what changed since the previous snapshot. In a long agent loop, that changes the cost model. The first read describes the page; later reads describe transitions.</p>
<p>When the agent already knows the desired shape, it can skip the general reading step and request structured data:</p>
<pre><code class="language-bash">h5i browser extract '{"titles": ["h2"]}'
</code></pre>
<p>This is the interface I increasingly think agent browsers should expose: observable state, explicit actions, and compact transitions. Pixels can still be generated when visual information matters, but they do not need to be the default representation.</p>
<h2>Making every request accountable</h2>
<p>The second interface I wanted to reconsider was networking.</p>
<p>An agent does not merely visit the URL supplied by its user. A page can load subresources, open a WebSocket, submit a form, execute JavaScript, or redirect through several origins. Page content can also try to persuade the model to visit a private service or disclose information elsewhere.</p>
<p>For that reason, h5i treats each outgoing request as a policy decision.</p>
<p>Conceptually, the path is:</p>
<pre><code class="language-text">agent action -&gt; construct request -&gt; check policy -&gt; record receipt -&gt; send bytes
</code></pre>
<p>Redirects return to the policy check rather than inheriting approval from the initial URL. Allowed and denied requests are both visible in the session history. If h5i cannot write the receipt, it refuses to send the request.</p>
<p>The agent can inspect this state without leaving the browser workflow:</p>
<pre><code class="language-bash">h5i browser requests
h5i browser audit
</code></pre>
<p>The audit output connects browser actions, network activity, human handovers, and the way the session ended. Content returned from a page is fenced and labeled as untrusted before it is shown to the model.</p>
<p>This does not solve prompt injection. A model can still make a bad decision. The narrower claim is that a hostile page should not be able to bypass the browser's network policy, and that the resulting behavior should be reconstructable afterward.</p>
<h2>h5i vs Chromium</h2>
<p>To measure the overhead that motivated the project, I tested a cold read of a documentation-style page.</p>
<table>
<thead>
<tr>
<th>Engine</th>
<th>Cold read</th>
<th>Peak memory</th>
</tr>
</thead>
<tbody><tr>
<td>h5i</td>
<td>59 ms</td>
<td>65.6 MB</td>
</tr>
<tr>
<td>Chromium <code>headless_shell</code></td>
<td>176 ms</td>
<td>461.5 MB</td>
</tr>
</tbody></table>
<p>h5i was about <strong>3× faster</strong> and used <strong>86% less peak memory</strong>.</p>
<p>The practical question is not “which browser wins?” It is “how often does my agent need the complete browser platform?”</p>
<p>For one long-lived session, Chromium's startup cost may be irrelevant. For a service that creates many short-lived sessions to read pages, tens or hundreds of megabytes per session determine how many agents fit on the same machine.</p>
<h2>Reference</h2>
<ul>
<li><p><a href="https://h5i.dev/">https://h5i.dev/</a></p>
</li>
<li><p><a href="https://github.com/h5i-dev/h5i">https://github.com/h5i-dev/h5i</a></p>
</li>
<li><p><a href="https://github.com/boa-dev/boa">https://github.com/boa-dev/boa</a></p>
</li>
<li><p><a href="https://www.reddit.com/r/codex/comments/1w0trw7/i_built_a_headless_browser_for_ai_agents_entirely/">https://www.reddit.com/r/codex/comments/1w0trw7/i_built_a_headless_browser_for_ai_agents_entirely/</a></p>
</li>
<li><p><a href="https://www.reddit.com/r/vibecoding/comments/1w5ea1t/github%5C_h5idevh5i%5C_a%5C_headless%5C_browser%5C_purely/">https://www.reddit.com/r/vibecoding/comments/1w5ea1t/github\_h5idevh5i\_a\_headless\_browser\_purely/</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>