Skip to content

Act

Act

Fill, click and assert in your own signed-in browser, under explicit grants and with every step on record.

do runs a typed list of browser steps in your Chrome, through a registered identity. Reading never grants acting: every action needs its own grants, and nothing runs automatically.

  • Steps are fill, click, wait_for, assert_text and assert_value on CSS selectors.
  • Every call has an idempotency key. A completed key returns its saved result instead of running again.
  • Values and selectors stay out of receipts; before and after HTML and screenshots are kept privately as evidence.

Write the steps as an intent:

intent.json
{
"url": "https://www.example-shop.com/account/address",
"contract": "browser.raw_control.v1",
"action_class": "WRITE_EXTERNAL",
"idempotency_key": "update-address-2026-10-06",
"description": "Update the delivery address",
"actions": [
{ "tool": "fill", "selector": "#postcode", "value": "2000" },
{ "tool": "click", "selector": "button[type=submit]" },
{ "tool": "assert_text", "selector": ".notice", "value": "Saved" }
]
}
import json
from frankensurf import WebPolicy
policy = WebPolicy(
identity="shop-me",
action_classes=("WRITE_REVERSIBLE", "WRITE_EXTERNAL", "PURCHASE/FINANCIAL", "ACCOUNT_SECURITY"),
browser_do_allowed_contracts=("browser.raw_control.v1",),
browser_do_allowed_origins=("https://www.example-shop.com",),
browser_do_allowed_tools=("fill", "click", "assert_text"),
)
result = await web.do(json.load(open("intent.json")), policy, provider="browser_use_local_cdp_do")

browser.raw_control.v1 can do anything a click can do, so it needs the full set of grants, in both the call and the identity:

Grant What it must name
Contract browser.raw_control.v1
Provider browser_use_local_cdp_do
Origins Every origin the steps may touch
Tools Exactly the tools the intent uses
Action classes WRITE_REVERSIBLE, WRITE_EXTERNAL, PURCHASE/FINANCIAL, ACCOUNT_SECURITY

Register the identity with matching --allow-action grants.

A completed action means the steps ran as written and the assertions passed. The result always says semantic_result: "unknown": Frankensurf doesn’t claim the address was saved or the order placed. Check that with a read.

If a run fails part-way, its key becomes EXECUTION_OUTCOME_UNKNOWN and is never replayed, because the site may already have changed.