Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pinchtab/pinchtab/llms.txt

Use this file to discover all available pages before exploring further.

By default, PinchTab launches its own isolated Chrome instance. But sometimes you want to connect to an existing Chrome instead — to share one browser across multiple agents, save memory in container environments, or integrate with external Chrome setups. That’s what CDP_URL does.

What is CDP_URL?

CDP_URL is a WebSocket URL that points to Chrome’s DevTools Protocol server:
CDP_URL=ws://localhost:9222/devtools/browser/b041f900-...
./pinchtab
# Instead of launching Chrome, PinchTab connects to the existing instance at port 9222
When you set CDP_URL, PinchTab:
  • ✅ Skips launching its own Chrome
  • ✅ Connects to the browser at that URL
  • ✅ Creates tabs in the existing browser
  • ✅ Shares that browser’s session (cookies, localStorage, etc.)

Use Cases

Multi-Agent Resource Sharing

Problem: Multiple agents on the same machine each launching their own Chrome eats memory:
Agent 1 → launches Chrome (1.3GB)
Agent 2 → launches Chrome (1.3GB)
Agent 3 → launches Chrome (1.3GB)
──────────────────────────────────
Total: 3.9GB+ just for browser instances 😬
Solution: All agents share one Chrome via CDP_URL:
1

Start shared Chrome once

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 &
2

Get the CDP URL

CDP_WS=$(curl -s http://localhost:9222/json/version | jq -r '.webSocketDebuggerUrl')
3

All agents connect to it

export CDP_URL="$CDP_WS"
./agent-1
./agent-2
./agent-3
Benefit: 1.3GB for Chrome + lightweight agent processes. Save 2.6GB per agent.
Typical setup:
  • Agent A handles browser operations (creates/controls tabs)
  • Agent B handles data processing (reads via Agent A’s HTTP API)
  • Agent C handles orchestration (no browser needed)
All three can point to the same PinchTab instance instead of each running their own.

Integration Testing

Problem: Test scripts need to control the same browser in sequence:
./test-screenshot.sh
./test-login.sh
./test-checkout.sh
# Each script might have its own isolated Chrome
Solution: Start one Chrome, all scripts target it:
# Start test Chrome once
chrome --remote-debugging-port=9222 &

# All test scripts use the same browser
CDP_URL=ws://localhost:9222/... ./test-screenshot.sh
CDP_URL=ws://localhost:9222/... ./test-login.sh
CDP_URL=ws://localhost:9222/... ./test-checkout.sh
Benefits:
  • Persistent session across tests (log in once, reuse)
  • Simpler test setup (no per-test Chrome cleanup)
  • Faster iteration (no Chrome startup overhead per test)

Container/Docker Deployments

Problem: Chrome runs in one container, your application in another:
# Chrome container (separate)
FROM chromium:latest
EXPOSE 9222

# App container (separate)
FROM golang:latest
ENV CDP_URL=http://chrome-service:9222/...
RUN ./pinchtab
Solution: PinchTab connects to the Chrome container via CDP_URL:
docker run -d --name chrome \
  -p 9222:9222 \
  chromium:latest --remote-debugging-port=9222
Benefits:
  • Separate scaling (one Chrome for 10 PinchTab instances)
  • Simpler networking (Chrome container = stable endpoint)
  • Cleaner orchestration (containers talk via service names)

How to Use

Find the CDP URL

Start Chrome with --remote-debugging-port and query the endpoint:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 &
Get the WebSocket URL:
curl http://localhost:9222/json/version | jq -r '.webSocketDebuggerUrl'
# Output: ws://localhost:9222/devtools/browser/b041f900-b164-4ad2-872d-359456b4e198

Connect PinchTab

export CDP_URL="ws://localhost:9222/devtools/browser/b041f900-..."
./pinchtab
That’s it. PinchTab will:
  1. Connect to the existing Chrome
  2. Discover existing tabs (if any)
  3. Create new tabs as requested
  4. Serve the HTTP API normally

Multi-Agent Example

#!/bin/bash

# Start shared Chrome
chrome --remote-debugging-port=9222 &
CHROME_PID=$!

# Get the CDP URL
CDP_WS=$(curl -s http://localhost:9222/json/version | jq -r '.webSocketDebuggerUrl')

# Launch all agents pointing to the same Chrome
export CDP_URL="$CDP_WS"

# Start multiple agent instances
./agent-browser &        # Handles browser operations
./agent-processor &      # Processes data (uses HTTP)
./agent-orchestrator &   # Coordinates workflow

wait

Capabilities & Limitations

What works:
  • ✅ Creating tabs in existing Chrome
  • ✅ Navigating to URLs
  • ✅ Snapshots and actions
  • ✅ Session persistence (cookies are stored in Chrome’s profile)
  • ✅ Multiple PinchTab instances sharing one Chrome
What doesn’t work yet:
  • ❌ Launching Chrome from scratch (you have to start Chrome separately)
  • ❌ Profile management (profiles belong to the remote Chrome, not PinchTab)
Performance:
  • Slightly higher latency per request (WebSocket to remote Chrome instead of local IPC)
  • Memory savings: ~1.3GB per agent when sharing

Troubleshooting

Chrome might not have any windows open yet. Solution:
# Ensure Chrome has at least one window
chrome --remote-debugging-port=9222 --new-window &
PinchTab failed to find a valid target. Make sure:
  1. Chrome is running with --remote-debugging-port
  2. The CDP_URL is correct (test with curl)
  3. Chrome has at least one window/tab open
Local machine only:
export CDP_URL="ws://localhost:9222/..."
Docker/Kubernetes (inter-container):
export CDP_URL="ws://chrome-service:9222/..."  # Use service name
Remote machine:
# SSH tunnel (recommended for security)
ssh -L 9222:remote-chrome:9222 user@remote-host &
export CDP_URL="ws://localhost:9222/..."

# Or direct (less secure)
export CDP_URL="ws://remote-chrome.example.com:9222/..."

Configuration Reference

Env VarDefaultEffect
CDP_URL(none)WebSocket URL to remote Chrome. When set, PinchTab connects instead of launching
BRIDGE_PORT9867Local HTTP port (independent of Chrome port)
BRIDGE_HEADLESStrueIgnored when using CDP_URL (remote Chrome state is independent)
BRIDGE_PROFILE~/.pinchtab/chrome-profileSession storage directory (still used even with CDP_URL)
BRIDGE_NO_RESTOREfalseSkip restoring tabs from previous session

Security

Important: Chrome’s DevTools Protocol has no built-in authentication. Anyone with access to the CDP port can fully control Chrome — steal cookies, read pages, make transactions, access any logged-in account.

Local Machine (Safe)

If Chrome and PinchTab run on the same local machine:
# ✅ Safe — listens on localhost only by default
chrome --remote-debugging-port=9222
export CDP_URL="ws://localhost:9222/..."
What’s safe:
  • Only processes on your machine can access it
  • Other users on shared systems can still access it
  • Docker containers on same host can access it
Defense: Use file permissions on Chrome’s profile directory:
chmod 700 ~/.chrome-profile  # Only you can read

Network Exposure (Critical Risk)

Never expose the CDP port to the network:
# ❌ DANGEROUS — exposes to entire network
chrome --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222

# ❌ DANGEROUS — Docker exposes to host network
docker run -p 9222:9222 chromium:latest --remote-debugging-port=9222
What attackers can do with network access:
  • Read every page Chrome visits
  • Steal all cookies and auth tokens
  • Make purchases, send emails, access bank accounts
  • Execute arbitrary JavaScript
  • Access sensitive data in localStorage/IndexedDB

Safe Network Access: SSH Tunnel

If Chrome runs on a remote machine:
# On local machine, create secure tunnel
ssh -L 9222:localhost:9222 user@remote-host &

# Now PinchTab uses the tunneled connection (encrypted via SSH)
export CDP_URL="ws://localhost:9222/..."
./pinchtab
This way:
  • ✅ Communication is encrypted (SSH tunnel)
  • ✅ Remote Chrome is not exposed to the network
  • ✅ Only you can access it (requires SSH login)

Docker/Kubernetes Best Practices

# Kubernetes Pod with Chrome + PinchTab sidecars
spec:
  containers:
  - name: chrome
    ports:
    - containerPort: 9222  # No hostPort = not exposed to outside

  - name: pinchtab
    env:
    - name: CDP_URL
      value: "ws://localhost:9222/..."  # Talk within pod

Security Checklist

  • Chrome listens on 127.0.0.1 (localhost only) or is SSH-tunneled
  • CDP port (9222) is not exposed to the network/internet
  • No --remote-debugging-address=0.0.0.0 flag
  • Firewall blocks external access to the port (if on a server)
  • Chrome profile directory has restricted permissions
  • Only trusted agents/code have access to CDP_URL
  • In containers: Chrome service is ClusterIP, not LoadBalancer
Principle: Treat Chrome’s CDP port like you’d treat:
  • An SSH port (gives shell access to a machine)
  • A database port (gives data access)
  • An admin panel (gives system control)
Restrict access ruthlessly. Use SSH tunnels for anything remote.