Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save nazt/6cf4eb650742a61bac6dbb4c2f33c178 to your computer and use it in GitHub Desktop.

Select an option

Save nazt/6cf4eb650742a61bac6dbb4c2f33c178 to your computer and use it in GitHub Desktop.
OpenClaw Fleet v2: CDP Bypass Technical Writeup + Thai Gateway/Agent/Node Explainer

How I Bypassed a Broken Approval System with Two Lines of Socat

OpenClaw's headless nodes couldn't run shell commands. So I built a socat tunnel chain that gives me full browser control — navigate, screenshot, DOM snapshot — across 5 nodes in 3.7 seconds.

🇹🇭 เจอบัคที่สั่ง run คำสั่งไม่ได้ เลย bypass ด้วย socat ได้ browser control เต็มรูปแบบ 5 เครื่องใน 3.7 วินาที


The Problem

OpenClaw fleet v2 was live: 5 KVM nodes, 5 Chromium browsers, all connected to the gateway via WireGuard. The dashboard showed 5/5 green. Commands like system.which returned instantly.

But anything that needed exec approval — system.run, nodes run, any shell command — timed out at exactly 120 seconds. Every single time.

$ openclaw nodes run --node openclaw-node-1 --raw "hostname"
... 120 seconds later ...
Error: exec.approval.request timeout 120007ms

Root Cause: A Socket That Never Exists

After three debugging sessions, SSH'ing into nodes, reading logs, restarting services, switching from system to user systemd services — I found the root cause:

$ ls ~/.openclaw/exec-approvals.sock
ls: cannot access '.../exec-approvals.sock': No such file or directory

The file exec-approvals.json existed (with a */* wildcard allowlist). But the Unix socket exec-approvals.sock — which the node host process needs to create for IPC — was never created. The node host process on Linux headless simply doesn't create it.

The flow: nodes run → gateway → node → check approval socket → socket doesn't exist → wait for IPC response → timeout 120s.

My theory: the approval socket is designed for the macOS companion app (which has a GUI for approval prompts). The headless Linux node host was never updated to create it automatically.

The Discovery: What Actually Works

Before giving up on OpenClaw's invoke system, I mapped every browser.proxy path systematically:

# Tested every path from the openclaw browser CLI
for path in / /tabs /snapshot /status /navigate /screenshot /click /open ...; do
  openclaw nodes invoke --node openclaw-node-1 \
    --command browser.proxy \
    --params "{\"path\":\"$path\"}" --json
done

Results:

Path Status Returns
/ OK Browser status (running, cdpReady, version)
/tabs OK Open tabs with titles, URLs, WebSocket URLs
/snapshot OK Full DOM tree in AI-readable format
/console OK Recent console messages
/errors OK Page errors
/requests OK Recent network requests
/navigate Not Found
/screenshot Not Found
/click Not Found
/open Not Found
Everything else Not Found

browser.proxy is read-only. Despite the name "proxy", it only exposes 6 monitoring endpoints. No write operations. No navigation. No screenshots.

The Bypass: Socat Tunnel Chain

The key insight: each node has a perfectly working Chromium with CDP (Chrome DevTools Protocol) on localhost:18792. The only problem is routing — the gateway can't reach it.

Two hops of socat solve this:

Hop 1: Node exposes CDP to WireGuard interface

Each node's Chromium binds to 127.0.0.1:18792 (localhost only). A socat forward exposes it:

# On each node (or as systemd service):
socat TCP-LISTEN:18793,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:18792

Now 10.10.0.11:18793 (the node's WireGuard IP) reaches Chromium.

Hop 2: Gateway forwards to nodes via WireGuard

# On gateway — one per node:
socat TCP-LISTEN:18801,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.11:18793  # node-1
socat TCP-LISTEN:18802,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.12:18793  # node-2
socat TCP-LISTEN:18803,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.13:18793  # node-3
socat TCP-LISTEN:18804,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.14:18793  # node-4
socat TCP-LISTEN:18805,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.15:18793  # node-5

The Result

The gateway's openclaw browser CLI uses cdpUrl: http://localhost:18801. It thinks it's talking to a local Chromium. It's actually talking to node-1's Chromium — through two socat hops and a WireGuard tunnel:

openclaw browser navigate "https://example.com"
  → localhost:18801 (gateway socat)
  → 10.10.0.11:18793 (WireGuard tunnel)
  → 127.0.0.1:18792 (node-1 Chromium CDP)
  → Chrome navigates to example.com
  → {"ok": true, "url": "https://example.com/"}

The Demo: 5 Nodes, 5 Sites, 3.7 Seconds

# fleet-demo.py (runs on gateway)
sites = [
    ("node-1", 18801, "https://news.ycombinator.com"),
    ("node-2", 18802, "https://github.com/trending"),
    ("node-3", 18803, "https://www.bbc.com/news"),
    ("node-4", 18804, "https://en.wikipedia.org/wiki/Main_Page"),
    ("node-5", 18805, "https://httpbin.org/ip"),
]

# Connect to each node's CDP WebSocket, navigate, screenshot — all in parallel
results = await asyncio.gather(*[demo_node(n, p, u) for n, p, u in sites])

Output:

  OpenClaw Fleet Browser Demo
  Controlling 5 nodes via Chrome DevTools Protocol

  node-1: "Hacker News" (94KB screenshot)
  node-2: "GitHub Trending" (77KB screenshot)
  node-3: "BBC News" (174KB screenshot)
  node-4: "Wikipedia" (101KB screenshot)
  node-5: "DuckDuckGo" (138KB screenshot)

  5/5 nodes OK — completed in 3.7s

Five different websites. Five screenshots. Five DOM snapshots. All captured in parallel via CDP WebSocket connections through socat tunnels over WireGuard.

Control from Anywhere

From my MacBook (via SSH tunnel)

# SSH tunnel to gateway
ssh -f -N -L 18789:localhost:18789 root@152.42.206.137

# Navigate node-1's browser
openclaw browser navigate "https://example.com" \
  --url ws://localhost:18789 --token $TOKEN

# Take a screenshot
openclaw browser screenshot \
  --url ws://localhost:18789 --token $TOKEN

# Query any node's tabs directly
openclaw nodes invoke --node openclaw-node-3 \
  --command browser.proxy --params '{"path":"/tabs"}' \
  --url ws://localhost:18789 --token $TOKEN

From the Web Dashboard

Open http://localhost:18789/dashboard/chat — type natural language commands. The AI agent (GLM-5) takes screenshots, navigates, and describes what it sees.

From the Gateway CLI

openclaw browser navigate "https://example.com"
openclaw browser screenshot
openclaw browser snapshot

Making It Persistent

Ad-hoc socat dies on reboot. Systemd services don't:

Node side: /etc/systemd/system/cdp-socat-node@.service

[Unit]
Description=CDP socat forwarder (listen :%i -> localhost:18792)
After=chromium-cdp.service
Wants=chromium-cdp.service

[Service]
ExecStart=/usr/bin/socat TCP-LISTEN:%i,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:18792
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
systemctl enable --now cdp-socat-node@18793.service

Gateway side: individual services per node

# cdp-socat-gw-node1.service, cdp-socat-gw-node2.service, etc.
ExecStart=/usr/bin/socat TCP-LISTEN:18801,fork,reuseaddr,bind=127.0.0.1 TCP:10.10.0.11:18793

Architecture Diagram

MBA (macOS)
  │
  │ SSH tunnel (port 18789)
  │
  ▼
DO Gateway (152.42.206.137)
  │ OpenClaw Gateway (ws://localhost:18789)
  │
  │ cdpUrl → localhost:18801
  │
  ├── socat :18801 ──► WG 10.10.0.11:18793 ──► node-1 Chrome :18792
  ├── socat :18802 ──► WG 10.10.0.12:18793 ──► node-2 Chrome :18792
  ├── socat :18803 ──► WG 10.10.0.13:18793 ──► node-3 Chrome :18792
  ├── socat :18804 ──► WG 10.10.0.14:18793 ──► node-4 Chrome :18792
  └── socat :18805 ──► WG 10.10.0.15:18793 ──► node-5 Chrome :18792
                                                    │
                                          5x KVM VMs on black.local
                                          Ubuntu 24.04, Chromium 145
                                          Headless + Xvfb + noVNC

Gotchas

  1. Chrome 145 snap rejects Host: 127.0.0.1 — Always use localhost in cdpUrl, not 127.0.0.1
  2. Gateway caches CDP connections — Changing cdpUrl in config file requires gateway restart. No runtime switching.
  3. browser.proxy is read-only — Don't waste time trying write paths. Use direct CDP for full control.
  4. WebSocket URL port mismatch — CDP's /json/list returns ws://localhost:18792/devtools/page/... but you need to rewrite the port to match your tunnel port.

Cost Impact

Component Monthly Cost
DO Gateway $6 (s-1vcpu-2gb)
5 KVM VMs $0 (home server)
WireGuard $0
Socat $0
OpenClaw $0 (open source)
Total $6/month

The Lesson

When software has a broken internal routing system, don't spend days debugging the internals. Step back, look at what endpoints actually work (Chromium's CDP was always healthy), and bridge them externally with the simplest tool available.

Two lines of socat. That's all it took.


Part of the OpenClaw Fleet v2 series. Built with KVM, WireGuard, socat, and stubbornness. By Nat Weerawan + Homekeeper Oracle — 2026-02-23

OpenClaw: Gateway / Node / Agent คืออะไร? (ตอบ Somphop)

ตอบคำถาม: "ใช้ทำอะไรได้บ้าง แล้วน่าใช้ไหม (Gateway Agent Node สับสนไปหมด)"


สามตัวนี้คืออะไร?

ลองนึกภาพโรงงาน:

🏭 Gateway = ผู้จัดการโรงงาน
   รับออเดอร์ → มอบหมายงาน → ติดตามผล
   อยู่บน DigitalOcean Singapore ($6/เดือน)

🤖 Agent = หัวหน้าทีม (AI)
   รับโจทย์จากผู้จัดการ → คิดว่าต้องทำอะไร → สั่ง browser ทำงาน
   ใช้ LLM (GLM-5) ในการคิด

🖥️ Node = พนักงาน (เครื่องทำงาน)
   มี Chrome headless พร้อม → ทำตามคำสั่ง → ส่งผลกลับ
   5 เครื่อง VM บน server ที่บ้าน (ฟรี)

ทำไมต้องแยก 3 ส่วน?

ถ้าไม่แยก ถ้าแยก (แบบนี้)
เครื่องเดียวทำทุกอย่าง แต่ละเครื่องทำหน้าที่เดียว
พังหนึ่ง = พังหมด พังหนึ่ง = เครื่องอื่นยังทำงาน
scale ไม่ได้ เพิ่ม Node = เพิ่มกำลัง
ยึดติดกับ 1 IP แต่ละ Node มี IP, browser, identity ของตัวเอง

ใช้ทำอะไรได้บ้าง?

1. สั่ง Browser 5 เครื่องพร้อมกัน

ผม: "เปิด 5 เว็บพร้อมกัน"

node-1 → Hacker News
node-2 → GitHub Trending
node-3 → BBC News
node-4 → Wikipedia
node-5 → Example.com

ทั้ง 5 เว็บ: เปิด + screenshot + อ่านเนื้อหา
ใช้เวลา: 3.7 วินาที

ตัวอย่างจริง:

# จาก MacBook สั่ง node-1 เปิดเว็บ
openclaw browser navigate "https://news.ycombinator.com"

# ถ่ายภาพหน้าจอ
openclaw browser screenshot

# อ่าน DOM content
openclaw browser snapshot

2. Web Scraping / Monitoring

  • เปิดเว็บ → ดึงข้อมูล → เก็บผลลัพธ์
  • ทำซ้ำทุก X นาที ด้วย cron
  • ข้อดี: render JavaScript ได้ (ไม่เหมือน curl/wget)
  • ข้อดี: 5 เครื่องทำงานพร้อมกัน = เร็วกว่า 5 เท่า

3. AI Agent ทำงานอัตโนมัติ

พิมพ์ใน Dashboard Chat:

"ไปดู trending repos บน GitHub แล้วสรุปมาให้"

Agent (GLM-5) จะ:

  1. เปิด GitHub Trending
  2. อ่านรายชื่อ repos
  3. สรุปเป็นภาษาไทย
  4. ส่งกลับมาให้เรา

4. ตัวอย่างใช้งานจริงที่เป็นไปได้

Use Case วิธีทำ
เช็คราคาสินค้า 5 เว็บพร้อมกัน แต่ละ node เปิดคนละเว็บ, ดึงราคา, เปรียบเทียบ
Monitor เว็บไซต์ downtime cron ให้ node เปิดเว็บทุก 5 นาที, alert ถ้า error
เก็บข้อมูลข่าว 5 nodes = 5 แหล่งข่าว, scrape พร้อมกัน
ทดสอบเว็บจาก IP ต่างกัน แต่ละ node มี IP ของตัวเอง
Fill form อัตโนมัติ Agent สั่ง click, type, submit

สถาปัตยกรรม (แบบง่าย)

MacBook Air (ที่บ้าน)
    │
    │  SSH tunnel
    │
    ▼
DigitalOcean Singapore ($6/เดือน)
 ┌──────────────────────────┐
 │  Gateway                  │
 │  - รับคำสั่งจาก CLI/Web   │
 │  - ส่งงานให้ Node          │
 │  - Agent (AI) อยู่ที่นี่    │
 └──────────┬───────────────┘
            │  WireGuard (encrypted)
            │
 ┌──────────▼───────────────┐
 │  Server ที่บ้าน (black.local) │
 │  ┌─────┐ ┌─────┐ ┌─────┐│
 │  │ N-1 │ │ N-2 │ │ N-3 ││
 │  │ 🌐  │ │ 🌐  │ │ 🌐  ││
 │  └─────┘ └─────┘ └─────┘│
 │  ┌─────┐ ┌─────┐        │
 │  │ N-4 │ │ N-5 │        │
 │  │ 🌐  │ │ 🌐  │        │
 │  └─────┘ └─────┘        │
 │  5x VM, แต่ละตัวมี Chrome │
 └──────────────────────────┘

Demo ที่ทำได้จริงวันนี้

ถ่ายภาพหน้าจอจาก 5 เครื่อง

เราสั่งจาก MacBook ให้แต่ละ node เปิดเว็บต่างกัน แล้วถ่ายภาพหน้าจอมาดู:

Node เว็บที่เปิด ขนาดภาพ
node-1 Hacker News 91 KB
node-2 GitHub Trending 27 KB
node-3 BBC News 172 KB
node-4 Wikipedia 101 KB
node-5 Example.com 16 KB

ใช้เวลาทั้งหมด 3.7 วินาที (ทำพร้อมกัน 5 เครื่อง)

ดู DOM Content (ข้อความในหน้าเว็บ)

# ดูว่า node-1 เปิดเว็บอะไรอยู่
openclaw nodes invoke --node openclaw-node-1 \
  --command browser.proxy --params '{"path":"/tabs"}'

# ผลลัพธ์:
# Title: Hacker News
# URL: https://news.ycombinator.com/
# ดู DOM content ของ node-4
openclaw nodes invoke --node openclaw-node-4 \
  --command browser.proxy --params '{"path":"/snapshot"}'

# ผลลัพธ์:
# - heading "Welcome to Wikipedia," [level=2]
# - text: the free encyclopedia that anyone can edit.
# - text: 7,141,365 articles in English

ใช้ Dashboard Chat (พิมพ์ภาษาอังกฤษ)

เปิด http://localhost:18789/dashboard/chat แล้วพิมพ์:

take a screenshot and tell me what you see

AI Agent จะถ่ายภาพ แล้วอธิบายว่าเห็นอะไร


น่าใช้ไหม?

น่าใช้ ถ้า:

  • ต้องการ browser automation หลายเครื่อง — เช็คราคา, scrape ข้อมูล, monitor เว็บ
  • มี server อยู่แล้ว — ใช้ KVM สร้าง VM ฟรี ไม่มีค่าใช้จ่ายเพิ่ม
  • ต้องการ AI agent ควบคุม browser — พิมพ์สั่งเป็นภาษาคน แทนเขียน code
  • ต้องการ IP หลายตัว — แต่ละ node มี identity แยก

ยังไม่ต้องรีบ ถ้า:

  • ใช้ browser เดียวก็พอ — Puppeteer หรือ Playwright ตัวเดียวง่ายกว่า
  • ไม่มี server — ต้องลงทุน hardware ก่อน หรือใช้ cloud ทั้งหมด (แพงกว่า)
  • ไม่ต้องการ AI — ถ้าเขียน script เองได้ ไม่จำเป็นต้องมี Agent

ต้นทุน

รายการ ราคา
DO Droplet (gateway) ~$6/เดือน
Server ที่บ้าน + VM ฟรี (ถ้ามีอยู่แล้ว)
WireGuard + socat ฟรี
OpenClaw CLI ฟรี (open source)
AI Model (GLM-5) ตาม usage ของ ZAI API
รวม ~$6/เดือน + ค่า AI

สรุปสั้น ๆ

Gateway = ศูนย์กลางรับคำสั่ง (อยู่บน cloud)
Agent   = AI ที่คิดและสั่งงาน (อยู่บน Gateway)
Node    = เครื่องที่มี Chrome ทำงานจริง (อยู่ที่บ้าน)

ผลลัพธ์: สั่งจาก MacBook → AI คิด → 5 Chrome ทำงานพร้อมกัน → ได้ผลใน 3.7 วิ

ถ้ามี server อยู่แล้ว + ต้องการ browser automation หลายเครื่อง = น่าลอง


By Nat Weerawan + Homekeeper Oracle — 2026-02-23 OpenClaw Fleet v2: 5 nodes, WireGuard mesh, CDP browser control

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment