brw v0.4.0
TL;DR: hardens the loopback surfaces (CSRF, DNS-rebinding, extension auth), makes the extension bridge survive high concurrency (backpressure, reconnect, per-tab serialization), and fixes snapshot-on-action and tab handling. Upgrading the daemon does not require reinstalling the extension.
Breaking changes
- Extension bridge caps concurrent operations at 6 by default. Beyond the cap, calls queue; past the operation deadline they return
extension bridge busy, retry with backoffinstead of running. Tune with--bridge-max-inflight(envBRW_BRIDGE_MAX_INFLIGHT), 0 disables. Concurrency was previously unbounded. - HTTP control plane rejects cross-origin browser requests (CSRF) and, on a loopback bind, a non-allowlisted
Host(DNS-rebinding). Loopback CLI, MCP, and curl send no browserOriginand are unaffected; a browser-based API consumer or a customHostgets 403. - Seamless bridge defaults:
focus_tabno longer raises the OS window, andbrw_openplaces tabs in a defaultbrwtab group. Restore with--bridge-raise-windowand--bridge-tab-group "".
Security
- Per-launch extension bridge auth token: minted each start, persisted 0600 at
~/.brw/bridge-token, served only over loopback. Non-breaking by default (a wrong token is rejected; an older extension that sends no token still connects, logged once). SetBRW_BRIDGE_REQUIRE_TOKEN=1to require it once every extension is reloaded. - navpolicy normalises hosts to ASCII/punycode, so a Unicode host and its
xn--form hit the same allow/deny rule (closes a blocklist-evasion gap). - Extension refuses every cookie CDP method and the whole
Storagedomain; cookies and passkeys cannot be read through brw even by a rogue local socket peer. - Non-browser (empty-Origin) websocket clients are rejected at the bridge.
Bridge resilience under load
- Backpressure on the shared socket with a typed busy signal so callers can back off (see breaking #1).
- Reconnect-aware: an RPC arriving during the MV3 service-worker reconnect gap waits for the socket instead of failing "not connected". Idempotent reads retry once on a transient drop; mutating ops never auto-retry.
- Per-tab serialization: operations on one tab no longer interleave their CDP frames; distinct tabs still run in parallel.
- WS frame write bound to its own deadline, so a cancelled or slow request can no longer tear down the shared socket.
/statusreports inflight, queued, busy_drops, retries.
Reliability
snapshot:trueon action tools now works across the MCP-server to bridge HTTP boundary (was dropped in the upstream-http topology).- WaitFor awaits a single in-page promise instead of polling a heavy condition, so concurrent waits no longer flood the debugger.
- Tab enumeration via
chrome.tabs.queryacross all window types (excludes only PWA/app and devtools). - Chrome stability: debugger detach on shutdown, graceful shutdown, profile guard, leak fixes.
- JS alert/dialog auto-dismiss.
Tools and usability
- New
brw_navigate_to: navigate an existing tab to a URL, wait for load, return an observation. snapshot:trueon all action tools includes a full PageSnapshot in the result.- Dynamic ref disambiguation for sibling elements sharing a name.
brw_fillerrors show the element tag and suggestbrw_type;brw_wait_foracceptspage_ready.