> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser Bridge Protocol

> Every WebSocket message the browser bridge accepts, with replies and error codes

The bridge at `ws://127.0.0.1:8765/ws` routes messages between CLI clients, the bridge's BrowserAgent and the Chrome extension.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Bridge Protocol"
        Client[💻 Client] --> Bridge[🌉 Bridge :8765]
        Bridge --> Agent[🧠 BrowserAgent]
        Bridge --> Ext[🧩 Extension]
    end

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Client input
    class Bridge,Agent process
    class Ext output
```

## Quick Start

**1. Register as the extension (JavaScript)**

```js theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const ws = new WebSocket("ws://127.0.0.1:8765/ws");
ws.addEventListener("open", () => {
  ws.send(JSON.stringify({ type: "register", client: "extension" }));
});
ws.addEventListener("message", (e) => console.log(JSON.parse(e.data)));
```

**2. Start a session as a CLI client (Python)**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import asyncio, json, websockets

async def main():
    async with websockets.connect("ws://127.0.0.1:8765/ws") as ws:
        print(await ws.recv())  # "connected" welcome
        await ws.send(json.dumps({"type": "start_session", "goal": "open example.com"}))
        print(await ws.recv())

asyncio.run(main())
```

***

## Message Catalog

| Type | Direction | Reply |
| - | - | - |
| `register` | Client → Server | `status: registered` or `INVALID_ROLE` |
| `start_session` | CLI → Server | `status` or `MISSING_GOAL`, `NO_EXTENSION`, `EXTENSION_IN_USE` |
| `observation` | Extension → Server | `action` or `NO_SESSION` |
| `stop_session` / `cancel_session` | Any → Server | Session status |
| `ping` | Any → Server | `{"type": "pong"}` |
| `heartbeat` | Extension → Server | `{"type": "heartbeat_ack", "status": "ok"}` |

***

## Roles

Each socket is an extension, a CLI, or neither, decided at connect time.

| Connection | Role |
| - | - |
| `chrome-extension://` origin | Extension |
| No `Origin` header | CLI by default; may `register` as `extension` or `cli` |
| Web origin | Cannot register — `INVALID_ROLE` |

See [Role Registration](https://praison.ai/docs/docs/features/browser/extension-architecture#role-registration) for the handshake.

***

## Error Codes

| Code | Meaning |
| - | - |
| `NO_EXTENSION` | No registered extension socket to run the session |
| `INVALID_ROLE` | `register` rejected for this socket or role |
| `UNKNOWN_TYPE` | Message `type` not recognised |
| `MISSING_GOAL` | `start_session` without a `goal` |
| `NO_SESSION` | `observation` for an unknown session |
| `EXTENSION_IN_USE` | Extension already owns a session |
| `PARSE_ERROR` | Message was not valid JSON |
| `PROCESSING_ERROR` | Unexpected server error |
| `START_AUTOMATION_FAILED` | Could not deliver `start_automation` to the extension |

***

## Best Practices

> **Register before start\_session**
>
> Wait for the `registered` status before sending `start_session` on the same socket.

> **Keep the socket alive**
>
> Send `heartbeat` about every 20 seconds from the extension.

> **Handle error replies**
>
> Branch on the `code` field, not the `error` text.

***

## Related

> **[Extension Architecture](https://praison.ai/docs/docs/features/browser/extension-architecture)**
>
> How the CLI, bridge and extension fit together

> **[Browser Agent Deep Dive](https://praison.ai/docs/docs/features/browser-agent-deep-dive)**
>
> Engines, modes and troubleshooting


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.