A pi extension that gives AI coding agents a real Chromium browser running inside a Bhatti microVM sandbox. Each browser session is fully isolated in its own Firecracker VM — the entire VM state (cookies, localStorage, IndexedDB, service workers, open tabs) can be snapshot and restored in ~50ms.
pi (coding agent)
└─ bhatti-browser extension
├─ Bhatti API → creates/resumes a Firecracker microVM
├─ Chromium in VM → exposes CDP on port 9222
├─ Playwright → connects over CDP via Bhatti's reverse proxy
└─ Screenshots → returned as base64 PNG to the agent
On first use, the extension boots a sandbox from scratch, waits for Chromium's CDP endpoint, and checkpoints the VM as browser-ready. Every subsequent browser_open resumes from that checkpoint — skipping boot entirely.
State snapshots (browser_save_state) checkpoint the entire VM. Restoring a state resumes the VM with all browser state intact: authenticated sessions, filled forms, JavaScript heap — everything.
- Bhatti CLI configured — run
bhatti setupor setBHATTI_API_URL+BHATTI_AUTH_TOKENenv vars - Bhatti server with a VM image that includes Chromium (headless, CDP on port 9222)
Clone into your pi extensions directory:
cd ~/.pi/agent/extensions/
git clone https://github.com/sahil-shubham/pi-bhatti-browser.git
cd pi-bhatti-browser
npm installThe extension is auto-discovered by pi via the pi.extensions field in package.json.
The extension registers 9 tools, all available to the agent during a session:
| Tool | Description |
|---|---|
browser_open |
Launch Chromium in a sandbox, navigate to a URL. Optionally restore a saved state. Returns a screenshot. |
browser_screenshot |
Capture the current page (or full scrollable page). |
browser_navigate |
Navigate to a different URL. Returns a screenshot. |
browser_click |
Click an element by CSS selector. Returns a screenshot. |
browser_eval |
Run JavaScript in the page context, return the result. |
browser_resize |
Resize the viewport (presets: mobile 375×812, tablet 768×1024, desktop 1280×720, wide 1920×1080). |
browser_save_state |
Snapshot the entire VM as a named state. Captures everything. |
browser_list_states |
List all saved browser state profiles. |
browser_delete_state |
Delete a saved state profile. |
browser_close |
Disconnect Playwright and destroy the sandbox. |
States are full VM checkpoints stored as Bhatti diff snapshots. They're named with a state. prefix internally (e.g., state.logged-in-github).
browser_open url="https://github.com"
→ boots from base checkpoint (~50ms)
browser_save_state name="github-authed"
→ snapshots the entire VM
browser_close
→ destroys the sandbox
browser_open url="https://github.com" state="github-authed"
→ resumes from the authed snapshot (~50ms)
→ cookies, sessions, localStorage all intact
The extension reads Bhatti credentials from (in order):
- Environment variables:
BHATTI_API_URLandBHATTI_AUTH_TOKEN - Config file:
~/.bhatti/config.yaml(created bybhatti setup)
# ~/.bhatti/config.yaml
api_url: https://your-bhatti-server:8080
auth_token: bht_your_api_key| Constant | Default | Description |
|---|---|---|
SANDBOX_CPUS |
2 | vCPUs allocated per browser sandbox |
SANDBOX_MEMORY_MB |
1024 | Memory in MB per sandbox |
CDP_PORT |
9222 | Chrome DevTools Protocol port inside the VM |
CDP_POLL_TIMEOUT_MS |
30000 | Max time to wait for CDP readiness |
MAX_EVAL_OUTPUT |
10000 | Truncation limit for browser_eval output |
The test suite (test.ts) runs the full lifecycle end-to-end against a real Bhatti server — no mocks.
npx tsx test.tsWhat it covers:
- Base checkpoint seeding (boot → checkpoint → destroy)
- Resume from checkpoint + Playwright CDP connection
- Navigate, screenshot, eval, click, resize
- Save state as VM checkpoint
- Close + destroy, then resume from saved state
- Verify persistence (page URL, cookies survive snapshot/restore)
- List and delete states
- Cleanup
Requires a running Bhatti server with a Chromium-enabled VM image.
- Playwright over CDP — connects via Bhatti's reverse proxy (
/sandboxes/:id/proxy/9222/...), which tunnels WebSocket traffic into the VM. No direct network access to the VM needed. - Checkpoint lifecycle — the base
browser-readycheckpoint is created once and reused. State checkpoints are diff snapshots on top of it (only dirty memory pages, ~52ms). - Cleanup — the
session_shutdownevent destroys the sandbox automatically. Checkpoints persist across sessions. - Cancellation — every tool respects the
AbortSignalfrom pi, checking between async operations and tearing down on abort.