BuildNot yet confirmed elsewhere1 publisher3 min readPublished
One boolean on the click step decides whether the agent's browser launches at all
Webhands refuses any recipe containing a write-marked click until the caller resends it with confirm:true. That check runs before Cloudflare provisions a browser, so no mutation ever gets caught half-executed.
The Engineer · Build desk

What happened
- A developer published Webhands, a computer-use agent that logs into dashboards with no usable API, drives them in a real headless browser, and refuses any write action without explicit confirmation.
- Its exported Step type is a union of four actions, and only click carries an optional write boolean, which makes that one flag the entire read-versus-write classification scheme.
- A recipe containing a write step returns ok:false with the error "recipe contains a write step; resend with confirm:true to execute", and the caller must resend the identical recipe to proceed.
- It runs on Cloudflare Workers with Browser Rendering, dropping to a dry mode that returns the plan it would have executed whenever the BROWSER binding is absent.
Why it matters
- constraint Approval granularity is the whole recipe, not the button, so anyone who wants per-mutation sign-off has to split write clicks across separate recipes rather than batching them.
- exposure Correctness now rests on whoever writes the recipe: mislabel a destructive button as a read and it executes with no prompt, which makes this a labelling review problem rather than a browser containment problem.
- decision Because dry mode returns a plan without a paid binding, the spend on Cloudflare Browser Rendering can be deferred until recipes are already authored and tested.
- capability Every run hands back a base64 screenshot of what the browser saw plus the ordered log of actions taken, which is the evidence an operator needs when a mutation is disputed after the fact.
The gate is a static predicate over a JSON document, not a runtime interceptor. `hasWriteStep` walks the recipe and returns true if any step is a click marked `write: true`; when `confirm` is absent, the handler returns an error and never opens the browser [10]. The ordering is the part worth copying. The refusal lands before the browser is provisioned, so there is no window in which a write step has partially executed and then gets caught [12].
That is a different boundary from the one people usually reach for. Sandboxing isolates the process from the host, but the authority here sits elsewhere: in the logged-in session. The author's framing is that handing an agent a real browser session gives it everything the logged-in human can do, including issuing a refund or canceling an order [3]. Filesystem containment does not extend to refund authority.
The arithmetic on the gate is worth spelling out, because it sets the granularity. The check is one boolean AND evaluated once over the whole recipe [9]. Confirmations required per run is therefore one, regardless of how many write clicks the recipe holds: six write-marked clicks and one write-marked click both unlock on the same `confirm: true` [21]. A confirmed mutation costs exactly two requests carrying the identical body, since you resend the same recipe to execute it [22] - the first refused before any browser starts, the second doing the work.
There is a hole in the classification. It is visible in the type union. `goto` is treated as inherently safe, and the author states that the only action which can change state is a click [6][7]. Any mutation reachable by navigating to a URL sits outside the gate by construction [23]. Plenty of dashboards put a confirm-by-link somewhere in the flow.
For this model to transfer to your stack, two things have to be true. Every state change you care about must be behind a click on a selector you enumerated, and recipes have to be reviewed like code, because the flag is a field in a request body rather than a property of the button it points at [19].
The degrade paths are the best engineering in the writeup. With no Anthropic key set, the prompt-based extractor returns the raw text slice instead of failing, so the pipeline still runs in development [15]. When the key is present it calls the Messages API with a Claude Haiku model, asks for JSON only, strips code fences, and parses, and on a parse failure it returns the unparsed text rather than discarding the result [16]. When the BROWSER binding exists but Browser Rendering is over quota or unprovisioned, the launch is wrapped so it becomes a clean error instead of a crash [18]. Three places where the obvious implementation throws and this one hands back something you can inspect.
If I were adopting it, I would start on the extraction path that takes no model at all: a list of `fields` with CSS selectors, scraped directly [14]. A stale selector fails loudly, while a Haiku extraction that drifts one field fails quietly, and quiet failures in a pipeline that also holds a refund button are the ones that cost money.
What to watch
- Whether the write flag moves server-side into a per-dashboard selector allowlist, so recipe authors stop being the trust boundary.
- Whether per-step confirmation tokens appear, which would stop one confirm:true from approving a multi-write batch wholesale.
- Whether callers actually check the returned mode field, since a dry result looks like a success and executes nothing.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence42
- Adoption
- Insufficient
- Hype gap+16
- Incentives55
- Confidence38
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Webhands is a computer-use agent that operates dashboards in a real headless browser, returns clean structured data, and refuses any write action unless the caller explicitly confirms it.
- [2]
The author's stated motivation is tools with no usable API: seller centers, supplier portals and 3PL dashboards where the only way to get the data is to log in and click through the UI like a human.
- [3]
The author writes that the moment you hand an agent a real browser session you have given it the power to do everything the logged-in human can do, including issuing a refund, confirming a shipment or canceling an order.
- [4]
The author states that most automation frameworks treat every action the same way, where a click is a click, and calls that the design choice he did not want.
- [5]
Webhands is driven by POSTing a recipe, which is an entry URL, an optional list of login and navigation steps, and an extraction spec.
- [6]
The exported Step type is a union of four actions: goto with a url, type with selector, text and optional secret, click with selector and optional write boolean, and waitFor with selector and optional timeoutMs.
- [7]
The author states that typing, waiting, navigating and reading are inherently safe, that the only action that can change state is a click, and that the write flag is therefore only available on click.
- [8]
If a step is marked as a write, the request is refused unless it also carries confirm: true.
- [9]
The gate is the single condition hasWriteStep(recipe) && !confirm, which returns ok: false, a mode of live or dry depending on env.BROWSER, an empty steps array, and the error string "recipe contains a write step; resend with confirm:true to execute".
- [10]
hasWriteStep returns true if any step is a click marked write: true, and when confirm is absent the function returns an error and never opens the browser.
- [11]
To execute the write, the caller resends the exact same recipe with "confirm": true, and reads never need confirmation because they cannot mutate anything.
- [12]
The check runs before the browser is provisioned, so there is no window in which a write step has partially executed and then gets caught; the refusal is up front and based purely on the shape of the recipe.
- [13]
A successful run returns three things: data as structured JSON, screenshotBase64 as a base64 PNG of exactly what the browser saw, and steps as a log of the actions actually taken, such as type into #email (secret) and click #signin.
- [14]
Extraction works two ways: a list of fields with CSS selectors scraped directly with no model involved, or a natural language extract.prompt that hands the page text to Claude and asks for JSON.
- [15]
If no Anthropic key is set, the prompt-based extractor returns the raw text slice instead of failing, so the pipeline still runs in development.
- [16]
When the key is present the extractor calls the Messages API with a Claude Haiku model, asks for JSON only, strips any code fences and parses, and if parsing fails it returns the unparsed text rather than throwing away the result.
- [17]
Webhands runs on Cloudflare Workers with Browser Rendering: in live mode the BROWSER binding is present and it drives a real headless browser via Cloudflare's Puppeteer, and in dry mode the binding is absent and it returns the plan it would have run, labelled as a dry result, instead of executing.
- [18]
Even when the BROWSER binding exists but Browser Rendering is over quota or unprovisioned, the launch is wrapped so it degrades into a clean error instead of crashing.
- [19]
The author states that the write gate protects against executing an unconfirmed mutation but does not understand what a given click means, because the write: true flag is set by whoever authors the recipe.
- [20]
The author says he can author and test recipes in dry mode without a paid binding, then flip to live when ready.
- [21]
Confirmations required per run is one regardless of how many write-marked clicks a recipe contains, so a recipe with six write clicks and a recipe with one both unlock on the same confirm: true.
- [22]
A confirmed mutation costs exactly two requests carrying the identical recipe body: the first refused before the browser starts, the second executing.
- [23]
Because goto is classified as inherently safe and only a click can carry the write flag, any state change reachable by navigating to a URL falls outside the confirmation gate.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toRead freely, confirm before writing: a safety model for computer-use agents
1 article · August 29, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Edge and Serverless RuntimesFollow
- Computer-use agentsFollow
- Browser AutomationFollow
- Agent Write GuardrailsFollow
- LLM Structured ExtractionFollow
Entities
- PuppeteerFollow
- TypeScriptFollow
- Cloudflare Browser RenderingFollow
- Cloudflare WorkersFollow
- WebhandsFollow
- AnthropicFollow
- Claude HaikuFollow