Browser Bridge · Apache-2.0

Let an agent log in without giving it the password.

Your agent needs to sign into a site. The obvious way is to hand it the credential and hope. The Browser Bridge is the other way: the agent drives the browser, asks whichcredential to use, and a separate process types it. The value never enters the agent's context, its logs, or its model provider's.

Install

npm install -g @1claw/browser-bridge

Apache-2.0, and the demo runs with no 1Claw account — a local login form, a real Chromium, and the agent's tool result printed so you can check the password is not in it.

git clone https://github.com/1clawAI/browser-bridge
cd browser-bridge && pnpm install && pnpm build
node packages/browser-bridge/examples/demo.mjs

The problem is not the browser

Driving a browser from an agent is a solved problem — Playwright, Puppeteer, browser-use and Stagehand all do it well. What none of them solves is the credential itself.

The moment a password is in your agent's process, it is one prompt injection away from being typed somewhere else, one log line away from your observability vendor, and one context window away from your model provider. Filtering the model's output does not help: by the time you are filtering, the value has already been somewhere it should not be.

So the bridge does not give it to the agent at all.

Fast enough to be unremarkable

A full lifecycle against a live site — create the account, sign in, capture the API key the site issues, and make a real request with it. No human at any step.

StageTimeWhat happens
Provision
register + store password
2.13sThe bridge signs up, generates the password and stores it.
Sign in
username + password + submit
1.14sThe bridge logs in. The agent never sees the password.
Capture
read + store the API key
0.80sThe bridge reads the key on a page the agent never lands on.
Use
request with the key injected
0.37sThe key is injected into the request. The agent passes only a city name.
End to end4.43sAccount created, signed in, key captured and used.

Measured against a live third-party site, not a local fixture. The bridge opens a fresh page for each credential operation, which is most of the cost — and the reason a listener the agent installed earlier has nothing to observe.

How it differs from Computer Use and browser-use

It mostly does not compete with them. Computer Use and browser-use answer how does the agent drive. The bridge answers how does a credential get used without the agent holding it — a different question, and one they leave open.

AspectAnthropic Computer Usebrowser-useBrowser Bridge
What it isA model that sees screenshots and issues mouse and keyboard actions.A library that drives a browser for an LLM agent.A credential layer that sits under either of them.
Who types the passwordThe model, as a keystroke action it chose.The library, from a value your code supplied.A separate process the agent cannot read from.
Where the value livesIn the action stream, and so in the model's context.In the agent process. `sensitive_data` keeps it out of the prompt by substituting placeholders — the process still holds it.Only in the bridge, wrapped and zeroed after use. Never in the agent's process at all.
If the agent is prompt-injectedIt can be told to read the field back, or to type the secret somewhere else.It can be told to navigate somewhere and submit what it holds.It can ask for a fill. It cannot choose the page, cannot read the field, and cannot collect the value.
RelationshipComplementary — Computer Use can drive; the bridge holds the credential.Complementary — browser-use is Playwright-based and connects straight through.Not a replacement for either. It is the part they leave to you.

browser-use is Playwright-based, so it connects to the bridge unchanged. Stock Puppeteer and Playwright clients do too — there is a test that drives both through the gate against a real Chromium on every commit.

How to use it

1. Say what may be typed, and where

A human step. Hosts are matched exactly — a wildcard is refused at creation rather than stored, because a stored *.example.com matches nothing while looking permissive.

1claw browser binding create acme-login \
  --vault <vault-id> --path acme/password \
  --login-url https://app.acme.example/login \
  --hosts app.acme.example

2. Point your framework at the bridge

Every command crosses the gate. Nothing else is attached to the browser.

await puppeteer.connect({
  browserWSEndpoint: bridge.url,
});

await chromium.connectOverCDP(bridge.url);

3. The agent asks; it does not receive

One argument. No URL, because the bridge navigates to the binding's own login page rather than letting the agent choose which page receives a credential. No value, because the agent never supplies or collects one.

// the agent's tool call
{ "binding_id": "acme-login" }

// what comes back — an outcome, never a secret
{ "status": "filled" }

What the gate refuses

Reading the field

During a fill the whole target is blocked, not just the input — and the typing happens on a page the agent has never scripted, so a listener installed earlier has nothing to watch.

Another agent's pages

Each client gets its own browser context. Listing is narrowed to that client's own pages, and attaching to another's is refused even with the id in hand.

Response bodies

The events that carry raw headers are not forwarded. Filtering a response after the fact does not help when the side effect is the exfiltration.

Where it stops

A signup behind a CAPTCHA is a site saying it wants a human, and getting past that is not something we build. Where a site puts a human check in the middle of a flow, the bridge stops there and asks you.

Everything before and after that gate automates cleanly — which is what the timings above measure, on a real site with a real signup.

Get started