Claw Relay — open source trust layer for AI browser agents
curl -fsSL https://bun.sh/install | bash # Bun runtime
npm install -g agent-browser # browser engine
brew install cloudflared # remote access (optional)
git clone https://github.com/AndreaGriffiths11/claw-relay.git
cd claw-relay/relay-server
bun install
cp config.example.yaml config.yaml # edit with your agents
bun src/index.ts config.yaml
cd claw-relay/relay-core
cargo build --release
./target/release/claw-relay-core ../relay-server/config.yaml
# macOS — quit Chrome completely first (Cmd+Q)
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-debug
# Then connect agent-browser
agent-browser connect http://localhost:9222
Or use ./start.sh which handles Chrome, the relay, and tunneling in one command.
server:
port: 9333
host: "0.0.0.0"
agents:
my-agent:
token: "pick-a-strong-secret"
scopes: ["read", "interact"]
allowlist: ["github.com", "*.example.com"]
rateLimit: 30
blocklist:
- "*.bank.com"
- "mail.google.com"
audit:
logFile: "./audit.jsonl"
logToStdout: true
dashboard:
port: 9334
adminToken: "your-admin-token"
Connect to ws://localhost:9333 (or your tunnel URL via wss://).
{"type": "auth", "token": "your-token", "agent_id": "my-agent"}
| Action | Scope | Example |
|---|---|---|
snapshot | read | {"type": "snapshot"} |
screenshot | read | {"type": "screenshot"} |
click | interact | {"type": "click", "ref": "e5"} |
type | interact | {"type": "type", "ref": "e3", "text": "hello"} |
fill | interact | {"type": "fill", "ref": "e3", "text": "hello"} |
press | interact | {"type": "press", "key": "Enter"} |
hover | interact | {"type": "hover", "ref": "e2"} |
select | interact | {"type": "select", "ref": "e7", "values": ["opt1"]} |
navigate | navigate | {"type": "navigate", "url": "https://..."} |
close | navigate | {"type": "close"} |
evaluate | execute | {"type": "evaluate", "js": "document.title"} |
// Success
{"type": "result", "action": "snapshot", "ok": true, "data": "..."}
// Error
{"type": "error", "code": "permission_denied", "message": "Agent lacks 'interact' scope"}
{"type": "error", "code": "site_blocked", "message": "mail.google.com is blocked"}
Start with read only. Add more when you trust the setup.
Each agent can have an allowlist (only these sites) and a global blocklist (never these sites). Supports glob patterns: *.github.com, docs.example.com. Blocklist is fully customizable.
The built-in dashboard runs on port 9334. Manage agents, view live connections, browse the audit log — all from the browser.
Authenticate with your adminToken from config.yaml, or pass it as ?token= in the URL.
cloudflared tunnel --url http://localhost:9333
You'll get a URL like https://random-words.trycloudflare.com. Connect via wss://. No account needed.
If both machines are on the same tailnet: ws://<tailscale-ip>:9333
ngrok http 9333
The tunnel only provides transport. Every connection still requires a valid agent token. Scopes, allowlists, blocklists, and rate limits all apply regardless of how the agent connects.
| Problem | Fix |
|---|---|
| YAML duplicate key error | You have two of the same key. Replace the line — don't add a second one |
| Chrome won't start with debugging | Quit Chrome completely (Cmd+Q) before launching with --remote-debugging-port |
EADDRINUSE on port 9333 | lsof -ti:9333 | xargs kill |
502 from tunnel | Relay server isn't running |
Connection refused | Set host: "0.0.0.0" in config if connecting remotely |
| Dashboard blank | Build the SPA: cd relay-server/dashboard && bun run build |
bun: command not found | curl -fsSL https://bun.sh/install | bash |