Skip to content

Browser agents

If your agent drives a browser, sign in as usual, then call session.attach(page). From then on, every request the page makes to the site (page loads, scripts, its own fetch calls) carries the agent token and a fresh signature. Requests to any other origin go out untouched, and your private key never enters the browser: the signing runs in your process and the browser only sees finished headers.

It works with a Playwright Page or BrowserContext (a context also covers popups and new tabs) and a Puppeteer Page, on a local Chromium or a cloud browser you reach over CDP.

kernel.ts
// npm install @trybage/sdk @onkernel/sdk playwright
import Kernel from "@onkernel/sdk";
import { chromium } from "playwright";
import { BageAgent } from "@trybage/sdk/agent";
const { issuer, client_id, private_jwk } = JSON.parse(process.env.BAGE_AGENT_KEY ?? "{}");
const agent = new BageAgent({ issuer, clientId: client_id, privateJwk: private_jwk });
const session = await agent.signIn({
site: process.env.SITE ?? "https://shop.example",
loginHint: process.env.PERSON ?? "alex@example.com",
scopes: ["browse", "cart"],
});
// A Kernel cloud browser (KERNEL_API_KEY), driven over CDP.
const kernel = new Kernel();
const remote = await kernel.browsers.create({ stealth: true, timeout_seconds: 300 });
try {
const browser = await chromium.connectOverCDP(remote.cdp_ws_url);
const page = browser.contexts()[0]?.pages()[0] ?? (await browser.newPage());
// Every request to the site now carries the token and a fresh signature. Other origins get nothing.
await session.attach(page);
await page.goto(new URL(process.env.PAGE ?? "/products", session.site).href);
console.log(await page.textContent("body"));
await browser.close();
} finally {
await kernel.browsers.deleteByID(remote.session_id);
}

attach returns a function that stops signing (await it in TypeScript and async Python). With async Playwright in Python, use await agent.sign_in_async(...) and detach = await session.attach(page).

  • The site must accept Bage on its pages, not only on its API, since page loads are signed too.
  • Tokens last 15 minutes. For a longer task, call signIn again and attach the new session. With an Always grant this is silent.
  • Each signed request makes a round trip through your process over CDP, so a remote browser is a little slower on the site’s requests. Other origins aren’t intercepted.
  • More hosts: if the site serves pages or its API from another host it accepts the token on, pass { extraOrigins: ["https://api.shop.example"] }.
  • Your own HTTP client: session.signedHeaders(method, url) (Python: session.signed_headers) returns the three headers for one request.