# What it actually takes to build agent infrastructure yourself
**作者**: harsehaj ⋆˙⟡
**日期**: 2026-07-21T15:46:11.000Z
**来源**: [https://x.com/harsehaj/status/2079593790814527998](https://x.com/harsehaj/status/2079593790814527998)
---

TL;DR: A web agent needs more than a browser. At scale it needs warm pools, isolation, an identity sites accept, observability, and a model gateway behind every decision. Each layer can be built, but together they're a standing system a team of senior engineers must own. This post breaks down all five layers and how to know when building it yourself is worth it.
# Build vs. buy: How to build agent infrastructure yourself
Every agent that does real work on the web needs hands to do it, and those hands are a browser. Not an HTTP client that fetches markup, but a real browser that runs the page's JavaScript, holds a session across navigations, renders the DOM the model reasons over, and gets through the login wall. The model decides what to do, but the browser is what actually renders the page.
Hands are only the start though. Often overlooked, but we actually bring a lot more to our desks when we open up our laptops than we think. To work the open web the way a human does, an agent needs the rest of a human’s faculties, too. It needs a passport that sites recognize and wave through, eyes on what happened when a run goes wrong, and the judgment or reasoning behind each click. Still, agents don’t have our biology, so underneath all of that is their own computer-equivalent, which is plumbing that keeps thousands of them running at once without leaking into each other.

Interact with this component on the actual blog site: browserbase.run/build-buy
Standing up the first browser is easy. Chromium and Playwright are open source and you already run containers, so an agent can be clicking through a flow by the end of the day.
Dare I say a weekend project, until you dream of production.
The one browser you were watching becomes thousands you don’t have more eyes for, and each one has to carry a passport that sites accept, reach pages built to turn it away, recover when a session dies mid-task, and record enough that you can debug the single run in ten thousand that failed. Don't get me wrong, you could build all of it with tools you already know, but it's a great deal more than launching Chromium.
The thing is that none of this is even your agent. The passport, pools, isolation, proxies, observability, and model routing are all a substrate between your agent and the web. So every quarter spent building and maintaining it all is also a quarter not spent on the part of a product that’s uniquely yours.
Let me unravel everything to consider when building infrastructure for agents to use the web like humans do.
# Warm pools: pre-booting browsers to kill cold-start latency
An agent puts the browser in the request path, so: the model picks an action → the browser carries it out → page is rendered back. This page is the only view the model has before the loop runs again. The browser's latency is the agent's latency, and cold starts mid-task means a running agent is stalled while something downstream waits on it.
Locally you’d never feel this, because you start Chromium once and it stays up, and if the one session crashes you can restart it by hand. At scale that hand disappears, and small numbers aren’t small anymore. A fleet of thousands of browsers with nobody watching any singular session, so a 0.1% crash rate that used to be a rounding error unfortunately becomes a steady stream of incidents across thousands of sessions. The leak I could’ve restarted away on my laptop turns into slow OOM across the fleet.
The cold-start cost sits on top of that. Pulling the image, launching Chromium, and waiting for it to accept commands takes seconds, and these seconds are dead time when an agent is blocked waiting for a browser to exist. The fix for this is to keep browsers prepped in advance, which is what a warm pool is. The browsers are already booted and idling, waiting for the next request.
Running this well comes down to three things.

The first is sizing. Too few warm browsers, and requests will queue behind cold starts, which was the latency you were trying to remove in the first place. Too many warm browsers means paying for browsers that sit doing nothing. The right number tracks your traffic, so the pool has to grow and shrink through the day rather than sit fixed.
The second is the idle ratio where the economics live. How many spare browsers you carry per active one depends on how spiky your traffic is, so a product with bursty, unpredictable load has to over-provision for its peaks and might keep one warm browser for every one in use. Spread enough independent traffic across the same pool and then bursts smooth out, so one spare covers several active sessions and the cost per session drops.
The ratio improves as volume climbs high.

Interact with this component on the actual blog site: browserbase.run/build-buy
The third piece is draining. Sessions are stateful and long-lived, so you can't shed capacity the instant that load drops in the way you would with stateless workers. Killing one mid-session kills live work. Scaling down means waiting for sessions to finish before you can reclaim their capacity, and running a deploy that cycles the fleet to drain and refill in stages.
All three move with your traffic, so none of it can just be set and forgotten. It’s iterative, which means the pool you tuned for today is mistuned the moment usage shifts.
This is the first layer. It’s really just ordinary autoscaling made harder by state, and the idle capacity you carry to stay responsive is a cost that never reaches zero.
# Isolation: sandboxing untrusted pages across a shared fleet
A browser is the one thing in your sandbox that’s designed to run untrusted code. Everything else executes code you wrote or reviewed, but a browser loads whatever page the agent navigates to and runs whatever JavaScript it serves, inside your own process and on your own computer.
Once again, this doesn’t hurt on your laptop, but it packs a punch once you’re spinning up thousands of browsers, when they’re on pages nobody has vetted and all sharing infrastructure. One compromised browser is a foothold on the host it shares with everyone else's. A malicious page exploits a Chromium bug to break out of the browser process, and on shared infrastructure, that escape reaches the sessions running in parallel.
This happens more often than you think. Chromium ships security fixes constantly, plenty rated high or critical, and some patched only after they were already being exploited in the wild. It’s quite the hurdle to be running the most attacked software on Earth, thousands of copies at a time against input you don't control.
You handle this in two parts.
The first is isolation itself. Every session needs a real boundary, so a page that breaks out of the browser hits a wall instead of the host. No infection! This means one browser per sandbox, ideally per lightweight VM rather than a shared kernel, so a renderer exploit that escapes Chromium still can't reach the machine or the sessions beside it. The naive version, many browsers sharing one container, is the version where one bad page takes down the whole crew.
Our compute layer runs on Firecracker, so our browsers stay isolated at the VM level without paying for a full VM each.
The second part to consider is patching. The CVEs never stop, so neither does the patching. Every release has to be tested and rolled across the fleet quickly, because the window between a fix going public and the exploit being weaponized is short. Public CVEs are a clear recipe for anyone who wants in during that window.

Interact with this component on the actual blog site: browserbase.run/build-buy
Soon you’ll notice that patching and stealth fight each other. If you've forked Chromium for stealth, every upstream security patch has to be reapplied on top of your fork before it ships, so the harder you lean on custom evasion, the more painful staying current becomes. Security and evasion end up being the same release pipeline fighting over the same binary.
# Identity: coherence and residential routin
Every site your agent visits evaluates it and asks two questions: who is this? Where are they coming from? A human clears both without noticing, while an agent has to earn both with a passport. The open web either waves an agent through or turns it away.
Locally, neither question gets asked and nobody's screening you. At scale, both are asked on every request, and a default setup fails them through challenges/CAPTCHAs, rate limits, and outright blocks.
## Who the site sees
A browser carries an identity on every visit, which is just the set of signals a site can read about who's connecting, and they span the whole stack. At the network level, the TLS handshake produces a fingerprint (JA3, or the newer JA4) and the HTTP/2 frame ordering forms a signature, both of which differ between real Chrome and most automation stacks. Higher up, a page can read navigator.webdriver, enumerate installed fonts and plugins, and hash a canvas or WebGL draw to see how the graphics stack renders. It can even watch behavior, like mouse paths and the timing between clicks.
Cleaning up the obvious tells is the easy part. Headless Chromium sets navigator.webdriver to true, ships a HeadlessChrome user-agent, exposes no plugins, and renders canvas with no entropy. You can clear each of these directly by forking Chromium and making changes.
The challenging part is coherence. Detection systems read every signal together, so a browser that’s advertising Chrome on Windows while carrying a Linux renderer string, mismatched font metrics, and a timezone that doesn't match its IP geolocation is easier to spot than an untouched headless browser, because no real device produces that combination.

You have to maintain identities where every signal agrees with every other one, from the GPU string to the OS to the fonts to the locale, and when you rotate them to spread traffic, each one has to stay just as consistent.
Then there are CAPTCHAs. Past a certain volume, some sessions get challenged no matter how clean the identity is, so you wire in a solving path, detect which challenge type you're facing, and return the answer back into the page.

Interact with this component on the actual blog site: browserbase.run/build-buy
## Where you connect from
A perfect identity still gives you away if it arrives from the wrong place. The second question points to the IP, and the default answer fails immediately since traffic straight out of your cloud carries a datacenter address. The ranges for every major provider are public, so a lot of sites treat anything from them as automated before the page even loads.
Getting through means routing through residential IPs, which are the kind ordinary people browse from, so the web reads it as normal traffic. One step forward and two steps back, because this now opens a sourcing and reputation problem. Those addresses have to come from somewhere, and the somewhere has to be one you can stand behind. Reputation isn't fixed either as addresses get flagged, whole pools get burned, and a noisy neighbour sharing your pool can degrade it for everyone else.
IP reputation behaves like an asset you maintain rather than one you buy once. It’s another moving piece. You rotate addresses, retire the flagged ones, replace capacity as a pool wears down, and keep checking where the addresses came from in the first place.

Interact with this component on the actual blog site: browserbase.run/build-buy
Both questions drift on their own schedule. Detection systems update whenever they like, pools decay, and an identity + network setup that worked this month will start collecting blocks next month, unless someone's watching the block rate and keeping both halves current.
# Observability: recording sessions and tracing model decisions
The first three layers get an agent onto the page and acting. The next part is seeing that they actually did. You can’t trust an agent that you can’t watch, which is why agent infrastructure needs eyes and logs. We go from black box → a debuggable workflow.
This is already the case locally. The browser is right there, so you can watch it click through the flow, and open devtools when something looks off. It’s free visibility, as long as it’s one session and one screen.
It goes dark at scale. The agents run on a machine you don’t have eyes on, fails at step #40 out of a 67-step task, and you’re presented with an incredibly detailed log line of “click didn’t land.” You can't see the modal that took care of the button or the redirect that fired first. To debug it you need to see what the browser saw, which means recording the session. That gets pretty pricey.
## Video versus DOM
There are two ways to record a session, and they fail in opposite directions.
Video encodes the actual pixels as the session runs. It captures everything exactly as it rendered, but encoding thousands of concurrent streams burns CPU on the same hosts running the browsers. So observability competes with the fleet for compute.
DOM reconstruction, on the other hand, records the DOM and its mutations instead, then replays them into a fresh page later, the way rrweb does. It’s much lighter, since you're storing a stream of changes rather than video, but it breaks precisely where the real web gets complicated. Nested iframes, shadow DOM, cross-origin frames, and canvas don't reconstruct cleanly, so the weird sessions on hostile pages (which need the most debugging) are most likely to replay wrong.
You have to pick your failure for your use-case here. Video costs compute and storage but shows the truth, while reconstruction is cheap but sometimes unreliable. Robust setups run both, so video where fidelity is important and then reconstruction everywhere else, which means two recording pipelines instead of one.

Interact with this component on the actual blog site: browserbase.run/build-buy
## Seeing why, not just what
Replaying the browser tells you what happened on the page. For an agent, this isn’t the important part. The agent acted because a model decided to, so when a run goes wrong, the decision should be questioned. Did the model misread the page, get a bad tool result, or perhaps hallucinate a button that wasn't there?
Answering this means tracing every model call and tool call lined up against the session, so you can see the page state that the model was reasoning over when it chose an action. Replay and trace have to be placed in one timeline because they each tell half the story separately. One way to approach this is to put session replay, network and event logs, as well as the model and tool traces in one view.
This all has to also be stored and indexed long enough to matter. Full recordings + per-step traces across a fleet are terabytes of data. The log you'll probably want is an old one from the run that broke last week, so that retention is key.
# Model gateway: routing, fallback, and caching across model providers
The browser is the hands, but the model is the judgment, and judgment runs on API calls to whatever labs you've wired in. Locally, this is one SDK, one key, one provider, and it works.
Then at scale, one provider isn’t necessarily enough. Different steps call for different models, since the cheap/fast one handles routing and the expensive one handles the hard reasoning. It’s rough being locked to a single model or lab when pricing, limits, or the frontier itself shifts every month.
And the moment you're calling more than one, you've signed up to run a gateway between your agent and all of them, no matter whose framework you're using.

See stagehand.dev/evals
Running this comes down to a few things.
The first is routing and keys. Every provider has its own SDK, auth, request shape, and response format. You can wire each provider directly into the agent, but it’s usually better to put a thin wrapper in front so the agent talks to one interface. This way, one internal call routes to whichever lab a given step needs, with the keys and quotas for all of them managed in one place instead of dispersed through the codebase.
This setup stays live even when a provider isn't. Once you hit a rate-limit or a lab degrades, the agents still shouldn’t stall mid-task. Gateway enables retries with backoff, and fallback to another model when the primary won't answer.
We all know costs creep up quickly. Agent runs are chatty, since a single task can be dozens of model calls, and at fleet volume, the token bill gets hefty. The gateway is also where you meter spend per run, and where caching becomes relevant. When a step repeats, the same page parsed the same way across thousands of runs, the gateway can return the action it already resolved instead of calling the model again. That skips the call entirely, cutting both latency and bill. It's great, and it's one more thing to build and maintain.

Interact with this component on the actual blog site: browserbase.run/build-buy
Agent infrastructure builder turned model plumber!
# What building it actually costs
So what does all of this cost to run? The figures below are illustrative - one plausible mid-size fleet, but the shape maps to whatever your real numbers are.
Let’s say you run eight browser containers around the clock to keep capacity ready. At the compute they need, that lands around $0.10 per container-hour, so eight of them running continuously is roughly $5,800 a year in raw infrastructure. This will actually show up on an invoice, and it’s pretty reasonable.
The next part gets muddier with tokens. At fleet volume the model bill racks up fast, and depending on how much you wind back with caching (the repeat steps the gateway can answer without calling a model), it runs into the same range as the compute or well past it. I'll roughly put a few thousand dollars a year at this scale, and more as you grow.
The real cost is that none of this runs itself. For each layer I walked through, the warm pool that has to be resized, the Chromium fork that has to be re-patched against each CVE, the identities that drift, the pools that get flagged, the replay pipeline, and the gateway, is a standing system that someone has to own. Probably a team of senior engineers, which costs somewhere around $220,000 a year each.
Of course, they won't spend all their time here, but it’s still a decent chunk. Even if infrastructure takes 10-20% of an engineering team’s year (which is pretty conservative), that's $22,000 to $44,000 a year in salary for just one engineer spent keeping browsers alive.

Interact with this component on the actual blog site: browserbase.run/build-buy
Don’t forget about compliance either. If you’re working with healthcare or finance, SOC 2 type II, maybe HIPAA, a pentest, and zero-retention guarantees must also be stood up with each as its own project (audits and upkeep included). BTW, none of that actually makes the agent better. It’s the cost of being allowed to run it for real use cases.
# When to build it anyway
Everything so far is a case for how much you would take on when you build this, which doesn’t mean you should never build it. There are real situations where owning the stack is right.
The clearest is when browser infrastructure is literally your product. If you sell stealth, proxies, or a browser platform of your own, the five layers are in fact the thing your team is building.
Past that, the honest reasons to build in-house tend to be strategic rather than technical:
- You have the engineering bandwidth and you want the control. A large team that can afford to own this, and has a deliberate reason to, isn't wrong to.
- You can't depend on outside providers. There are on-prem requirements, a policy against procuring external vendors, or a data boundary that forces everything to run inside your own walls. When the constraint is real, it outweighs every efficiency argument, and you build because you have to.
- You want it on your own hardware. Some teams want browsers on bare metal they own and control end to end.
Most build decisions aren't made for these reasons, though. It’s often because a demo worked on day one, and the real costs appeared later with scale. Even “being early” isn’t a compelling enough reason to build. If you're prototyping by running a few sessions to prove something out, the right move is usually to buy, ship the proof of concept on a provider, and revisit build-versus-buy once you actually know the scope of the problem. Building infrastructure to run a POC means doing some really expensive work before knowing if you even need it.

Interact with this component on the actual blog site: browserbase.run/build-buy
## Where to go from here
That's the whole map with five layers between your agent and the open web, each one buildable, and each one as a standing system once it's live. So build? Or buy?
A few things worth reading next:
- What is a headless browser → the foundation the whole stack sits on, if you want the primitive before the fleet.
- Forking Chromium for AI automation → why suppressing the identity tells means going all the way to the binary.
- The CAPTCHA arms race → a deeper look at the detection side of identity, from distorted text to full browser identity.
- TLS fingerprinting with JA3 and JA4 → how the network layer reads you before a page even loads.
- rrweb → the open-source DOM-reconstruction approach behind the lighter half of session replay.
You can read the full blog post with interactive components here.
⋆˚꩜。 harsehaj
## 相关链接
- [harsehaj ⋆˙⟡](https://x.com/harsehaj)
- [@harsehaj](https://x.com/harsehaj)
- [21K](https://x.com/harsehaj/status/2079593790814527998/analytics)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [Firecracker](https://www.browserbase.com/blog/what-is-firecracker)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [TLS handshake](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/)
- [JA3](https://blog.cloudflare.com/ja4-signals/)
- [forking Chromium](https://www.browserbase.com/blog/chromium-fork-for-ai-automation)
- [CAPTCHAs](https://www.browserbase.com/blog/why-captchas-are-getting-harder)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [rrweb](https://github.com/rrweb-io/rrweb)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [stagehand.dev/evals](https://stagehand.dev/evals)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [browserbase.run/build-buy](https://browserbase.run/build-buy)
- [What is a headless browser](https://www.browserbase.com/blog/what-is-a-headless-browser)
- [Forking Chromium for AI automation](https://www.browserbase.com/blog/chromium-fork-for-ai-automation)
- [The CAPTCHA arms race](https://www.browserbase.com/blog/why-captchas-are-getting-harder)
- [TLS fingerprinting with JA3 and JA4](https://blog.cloudflare.com/ja4-signals/)
- [rrweb](https://github.com/rrweb-io/rrweb)
- [full blog post with interactive components here.](https://browserbase.run/build-buy)
- [Upgrade to Premium](https://x.com/i/premium_sign_up)
- [11:46 PM · Jul 21, 2026](https://x.com/harsehaj/status/2079593790814527998)
- [21.8K Views](https://x.com/harsehaj/status/2079593790814527998/analytics)
- [View quotes](https://x.com/harsehaj/status/2079593790814527998/quotes)
---
*导出时间: 2026/7/22 08:39:58*
---
## 中文翻译
# 亲自构建代理基础设施究竟需要什么
**作者**: harsehaj ⋆˙⟡
**日期**: 2026-07-21T15:46:11.000Z
**来源**: [https://x.com/harsehaj/status/2079593790814527998](https://x.com/harsehaj/status/2079593790814527998)
---

TL;DR:Web 代理需要的不仅仅是浏览器。在规模化场景下,它需要热池、隔离、网站认可的身份证、可观测性,以及位于每个决策背后的模型网关。每一层都可以构建,但合在一起它们就构成了一套系统,需要由一组高级工程师来维护。本文将拆解所有五个层级,以及如何判断何时值得自己构建。
# 自建 vs. 购买:如何自行构建代理基础设施
每一个在网络上做实事的代理都需要一双“手”来完成工作,而这双手就是浏览器。不是获取标记语言的 HTTP 客户端,而是运行页面 JavaScript、在导航间保持会话、渲染模型据以推理的 DOM,并突破登录墙的真正浏览器。模型决定做什么,但浏览器是实际渲染页面的工具。
然而,这双手仅仅是开始。这一点常被忽视,但当我们打开笔记本电脑时,实际上带到桌面的东西远比我们意识到的要多。要像人类一样在开放网络上工作,代理也需要人类具备的其余能力。它需要一个网站认可并放行的“护照”,一双在运行出错时盯着发生了什么的“眼睛”,以及每次点击背后的判断或推理。尽管如此,代理并没有我们的生物学构造,因此在这一切之下是它们自己的计算机等价物——即让成千上万个代理同时运行而不互相泄漏的基础管道。

在实际博客网站上与该组件互动:browserbase.run/build-buy
启动第一个浏览器很容易。Chromium 和 Playwright 是开源的,你已经在运行容器了,所以代理可以在一天结束时点击完整个流程。
我敢说这是一个周末项目,直到你梦想着上生产环境。
你监控的那一个浏览器变成了成千上万个你无法顾及的浏览器,每一个都必须携带网站接受的护照,到达旨在将其拒之门外的页面,在任务中途会话死亡时恢复,并记录足够的信息以便你调试那一万次运行中失败的那一次。别误会,你完全可以用你已经熟悉的工具构建所有这些,但这比启动 Chromium 要多得多。
问题是,这些都不属于你的代理本身。护照、池、隔离、代理、可观测性和模型路由都是你的代理和网络之间的一个底层。因此,每一个花在构建和维护这一切上的季度,也都是一个没有花在产品中属于你独特部分的季度。
让我来梳理一下构建让代理像人类一样使用网络的基础设施时需要考虑的一切。
# 热池:预启动浏览器以消除冷启动延迟
代理将浏览器置于请求路径中,因此:模型选择一个动作 → 浏览器执行它 → 页面被渲染回来。这个页面是循环再次运行前模型拥有的唯一视图。浏览器的延迟就是代理的延迟,任务中途的冷启动意味着正在运行的代理被阻塞,等待下游的某些东西。
在本地你永远感觉不到这一点,因为你启动一次 Chromium 并让它保持运行,如果那个会话崩溃了,你可以手动重启它。在规模化场景下,这只“手”消失了,微小的数字也不再微小。成千上万个浏览器的机群,没有人盯着任何单个会话,所以过去只是一个舍入误差的 0.1% 的崩溃率,不幸地变成了跨数千个会话的持续事件流。我在笔记本电脑上可以通过重启解决的泄漏,变成了整个机群缓慢的内存溢出(OOM)。
冷启动成本在此基础上更是雪上加霜。拉取镜像、启动 Chromium 并等待它接受命令需要几秒钟,而当代理被阻塞等待浏览器就绪时,这些秒钟就是死时间。解决这个问题的办法是提前准备好浏览器,这就是热池。浏览器已经启动并闲置,等待下一个请求。
运行良好的热池归结为三件事。

第一是规模调整。热浏览器太少,请求就会排队等待冷启动,而这恰恰是你最初试图消除的延迟。热浏览器太多意味着要为那些闲置什么都不做的浏览器买单。正确的数量要跟踪你的流量,所以池必须在一天中增减,而不是固定不变。
第二是闲置率,这里涉及经济账。每个活跃浏览器需要携带多少备用浏览器取决于你的流量有多大的波动性,因此具有突发性、不可预测负载的产品必须为其峰值过度配置,可能会为每个正在使用的浏览器保留一个热浏览器。如果在同一个池中分散足够的独立流量,那么突发就会平滑下来,一个备用就可以覆盖多个活跃会话,每个会话的成本就会下降。
随着量级的攀升,比例会改善。

在实际博客网站上与该组件互动:browserbase.run/build-buy
第三个部分是排空。会话是有状态的且长寿命的,因此你不能像处理无状态工作器那样在负载下降的瞬间立即丢弃容量。在会话中途杀死它会杀死正在进行的工作。缩容意味着在回收容量之前等待会话完成,并且运行部署需要循环机群以分阶段排空和重新填充。
这三者都随你的流量而动,所以没有一个可以设置后就置之不理。它是迭代的,这意味着你今天调整好的池,一旦使用情况发生变化,立刻就会失调。
这是第一层。实际上,这只是状态让普通自动扩展变得更困难,而你为了保持响应能力所承载的闲置容量是一项永远不会降到零的成本。
# 隔离:在共享机群中隔离不受信任的页面
浏览器是你的沙盒中设计用来运行不受信任代码的那个东西。其他一切都执行你编写或审查过的代码,但浏览器加载代理导航到的任何页面,并在你自己的进程和你自己的计算机上运行其提供的任何 JavaScript。
同样,这在你的笔记本电脑上不会造成伤害,但一旦你启动数千个浏览器,当它们位于没人审查的页面上并且共享基础设施时,它就会产生巨大的影响。一个被攻陷的浏览器就是通往其宿主(与所有人共享)的立足点。恶意页面利用 Chromium 漏洞从浏览器进程中逃逸,而在共享基础设施上,这种逃逸会触及并行运行的会话。
这种情况发生的频率比你想象的要高。Chromium 持续发布安全补丁,许多被评为高危或严重,有些甚至只有在野外已经被利用后才会修补。运行地球上遭受攻击最多的软件,一次数千份,面对你无法控制的输入,这确实是一个巨大的障碍。
你分两部分来处理这个问题。
第一是隔离本身。每个会话都需要一个真正的边界,因此逃出浏览器的页面遇到的是一堵墙而不是宿主。没有感染!这意味着每个沙盒一个浏览器,理想情况下是每个轻量级 VM 而不是共享内核,因此即使渲染器利用逃逸了 Chromium,也无法到达机器或旁边的会话。幼稚的版本,即多个浏览器共享一个容器,就是一个坏页面搞垮整组的版本。
我们的计算层运行在 Firecracker 上,因此我们的浏览器在 VM 级别保持隔离,而无需为每个浏览器支付完整的 VM 费用。
第二部分要考虑的是打补丁。CVE 永不停止,因此打补丁也不能停。每个发布版本都必须经过测试并快速推送到整个机群,因为修复公开与漏洞被武器化之间的窗口期很短。公共 CVE 对任何想在此期间入侵的人来说都是一个明确的配方。

在实际博客网站上与该组件互动:browserbase.run/build-buy
很快你就会注意到,打补丁和隐身是相互冲突的。如果你为了隐身而分叉了 Chromium,那么每次上游安全补丁都必须在你的分叉之上重新应用,然后才能发布,因此你越依赖自定义规避,保持最新状态就越痛苦。安全和规避最终变成了争夺同一个二进制文件的同一发布流水线。
# 身份:连贯性和住宅路由
你的代理访问的每个网站都会评估它并问两个问题:这是谁?他们来自哪里?人类毫不费力地通过这两项检查,而代理必须用护照来赢得这两项。开放网络要么放行代理,要么将其拒之门外。
在本地,两个问题都不会被问到,也没有人审查你。在规模化场景下,每个请求都会被问到这两个问题,而默认设置会通过挑战/CAPTCHA、速率限制和彻底拦截来让它们失败。
## 网站看到了谁
浏览器在每次访问时都携带一个身份,这只是一组网站可以读取的关于连接者的信号,它们跨越了整个协议栈。在网络级别,TLS 握手产生一个指纹(JA3 或较新的 JA4),HTTP/2 帧排序形成一个签名,这两者在真正的 Chrome 和大多数自动化栈之间都是不同的。在更上层,页面可以读取 navigator.webdriver,枚举已安装的字体和插件,并对 canvas 或 WebGL 绘图进行哈希处理,以查看图形栈如何渲染。它甚至可以观察行为,如鼠标路径和点击之间的时间间隔。
清理明显的泄漏特征是容易的部分。无头 Chromium 将 navigator.webdriver 设置为 true,发送 HeadlessChrome 用户代理,不暴露插件,并渲染没有熵的 canvas。你可以通过分叉 Chromium 并进行更改来直接清除每一个。
具有挑战性的部分是连贯性。检测系统会一起读取每个信号,因此一个广告自己是 Windows 上的 Chrome 但携带 Linux 渲染器字符串、不匹配的字体指标以及与其 IP 地理位置 不匹配的时区的浏览器,比未触动的无头浏览器更容易被发现,因为没有真实的设备会产生这种组合。

你必须维护每个信号都与其他信号一致的身份,从 GPU 字符串到操作系统、字体再到语言环境,当你轮换它们以分散流量时,每一个都必须保持同样的一致。
然后是 CAPTCHAs。超过一定量后,无论身份多么干净,某些会话都会受到挑战,因此你需要接入一个解决路径,检测你面临的是哪种挑战类型,并将答案返回到页面中。

在实际博客网站上与该组件互动:browserbase.run/build-buy
## 你从哪里连接
如果来自错误的地点,完美的身份也会暴露你。第二个问题指向 IP,而默认答案立即失败,因为直接从你的云发出的流量承载的是数据中心地址。每个主要提供商的地址范围都是公开的,因此许多网站甚至在页面加载之前就将来自它们的任何内容视为自动化流量。
通过意味着通过住宅 IP 进行路由,这是普通人浏览网络时使用的 IP 类型,因此 Web 将其视为正常流量。进一步退两步,因为现在这打开了一个采购和声誉问题。这些地址必须来自某个地方,而且那个地方必须是你能够支持的。声誉也不是固定的,因为地址会被标记,整个池会被烧毁,而共享你池子的一个吵闹的邻居可能会降低其他所有人的声誉。
IP 声誉的行为就像你维护的一项资产,而不是你一次性购买的东西。这是另一个移动的部分。你轮换地址,淘汰被标记的地址,在池磨损时替换容量,并不断检查地址最初来自哪里。

在实际博客网站上与该组件互动:browserbase.run/build-buy
这两个问题都有自己的漂移时间表。检测系统随时更新,池会衰减,除非有人监视拦截率并使两部分保持最新,否则本月有效的身份 + 网络设置下个月就会开始收集拦截。
# 可观测性:记录会话和追踪模型决策
前三个层级让代理进入页面并采取行动。下一部分是看到它们实际做了什么。你不能监视一个你无法观看的代理,这就是为什么代理基础设施需要眼睛和日志。我们要从黑盒 → 可调试的工作流程。
在本地已经是这种情况。浏览器就在那里,所以你可以看着它点击流程,并在看起来不对劲时打开 devtools。这是免费的可见性,只要它是一个会话和一个屏幕。
在规模化场景下情况变得漆黑一片。代理运行在你无法看到的机器上,在 67 步任务的第 40 步失败,而你面对的是一条极其详细的日志“点击未落地”。你无法看到接管按钮的模态框或首先触发的重定向。要调试它,你需要看到浏览器看到的东西,这意味着记录会话。这变得非常昂贵。
## 视频与 DOM
记录会话有两种方式,它们在相反的方向上失败。
视频在会话运行时对实际像素进行编码。它完全按照渲染效果捕获所有内容,但编码数千个并发流会在运行浏览器的同一宿主机上消耗 CPU。因此可观测性与机群争夺计算资源。
另一方面,DOM 重构记录 DOM 及其变化,然后像 rrweb 那样在稍后将它们重放到一个新页面中。它要轻得多,因为你存储的是变化流而不是视频,但它在真正的 Web 变得复杂的地方恰恰会出问题。嵌套的 iframe、shadow DOM、跨域框架和 canvas 无法干净地重构,因此敌对页面上的奇怪会话(最需要调试的)最可能重放错误。
在这里你必须根据你的用例选择你的失败方式。视频消耗计算和存储但显示真相,而重构便宜但有时不可靠。强大的设置两者都运行,在保真度重要的地方使用视频,其他地方使用重构,这意味着两条记录管道而不是一条。

在实际博客网站上与该组件互动:browserbase.run/build-buy
## 看到为什么,而不仅仅是什么
重放浏览器告诉你页面上发生了什么。对于代理来说,这不是重要的部分。代理采取行动是因为模型决定这样做,因此当运行出错时,应该质疑的是决策。模型是否误读了页面?得到了错误的工具结果?还是产生了一个不存在的按钮的幻觉?
回答这个问题意味着追踪与会话排列的每个模型调用和工具调用,这样你就可以看到模型选择动作时正在推理的页面状态。重放和追踪必须放在一个时间线上,因为它们分别只告诉了一半的故事。解决这个问题的一种方法是将会话重放、网络和事件日志以及模型和工具追踪放在一个视图中。
所有这些还必须存储和索引足够长的时间以发挥作用。整个机群的完整记录 + 每步追踪是数 TB 的数据。你可能想要的日志是上周那个破坏运行的旧日志,因此保留是关键。
# 模型网关:跨模型提供商的路由、回退和缓存
浏览器是手,但模型是判断