ระบบ Peer
Peer-to-peer บน LAN — ค้นหา Clew instances อื่น, ส่งข้อความ, มอบหมายงาน, และรันคำสั่งระยะไกล
ระบบ peer อยู่ใน src/peer/ และประกอบด้วย 3 ชั้นหลัก: PeerServer (HTTP server), PeerDiscovery (สแกน LAN), และ PeerStore (registry ในหน่วยความจำ)
สถาปัตยกรรม
┌─────────────────────────────────────────────────────────────────────────────┐
│ PEER SYSTEM FLOW │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────┐
│ /peer share │
│ (Machine A — Worker) │
└────────────┬─────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PeerServer │ │PeerDiscovery │ │ PeerStore │
│ HTTP :random │ │ │ │ (in-memory) │
│ port │ │ │ │ │
└──────┬───────┘ └──────┬───────┘ └──────────────┘
│ │
│ ┌────────────┼────────────┐
│ │ │ │
│ ▼ ▼ ▼
│ ┌────────┐ ┌────────┐ ┌─────────────┐
│ │ File │ │ UDP │ │ PeerStore │
│ │ ~/.cl/ │ │multicast│ │ (singleton) │
│ │ peers/ │ │239... │ │ │
│ │{pid}. │ │:42069 │ │ │
│ │ json │ │ │ │ │
│ └────┬───┘ └────┬───┘ └─────────────┘
│ │ │
│ │ │ heartbeat ทุก 30s
│ │ │ stale timeout 90s
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ LAN NETWORK │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Machine B│◄───────►│ Machine C│ ... │
│ │ Clew Code│ query │ Clew Code│ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
└────────┼────────────────────┼─────────────────────────┘
│ │
│ /peer discover │ /peer discover
▼ ▼
═══ WORKER (Machine A) ENDPOINTS ═══
Peer อื่น POST /peer-info ──► { hostname, ip, cwd, shell, ... }
│ POST /peer-msg ──► รับข้อความ chat
├───────────────► POST /peer-todo ──► รับ task (ไปอยู่ใน inbox)
│ POST /peer-exec ──► รัน shell command + คืน stdout/stderr
═══ CLIENT (Machine B) TOOLS ═══
/peer discover ──► scan files + UDP query (3s timeout)
│
▼
[peer list]
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
/peer join /peer send_task /peer run
(POST /peer-info) (POST /peer-todo) (POST /peer-exec)
│
▼
/peer send_message ──► POST /peer-msg ──► inbox ปลายทาง
/peer broadcast ──► POST /peer-msg ──► ทุก peer ที่เชื่อมต่อ
/peer ping ──► GET /peer-info ──► เช็ค alive
═══ DAEMON (agentLoop.ts) ═══
┌───────────────────────────────────┐
│ Autonomous Agent Loop │
│ - ฟัง /peer-todo ตลอด 24/7 │
│ - รับ task จาก remote peers │
│ - execute ใน background worker │
│ - สูงสุด 3 workers พร้อมกัน │
└───────────────────────────────────┘
═══ DATA LIFECYCLE ═══
advertise ──► heartbeat ทุก 30s
│
├── 90s ไม่มี heartbeat ──► stale → evict
│
stop share ──► send UDP "offline"
delete peer file
close HTTP server
เทคนิคและหลักการ
Discovery แบบสองชั้น (Two-Layer Discovery)
ทำไมไม่ใช้แค่ UDP multicast หรือแค่ file-based อย่างเดียว?
- File-based — ทำงานบนเครื่องเดียวกันได้ทันที โดยไม่ต้องใช้ network เลย แค่อ่าน/เขียนไฟล์ JSON ใน
~/.claude/peers/ซึ่งเร็วกว่าและไม่ต้องกังวลเรื่อง firewall - UDP Multicast — ข้ามเครื่องได้ผ่าน LAN ใช้ multicast group
239.255.37.37:42069ไม่ต้องรู้ IP ปลายทางล่วงหน้า - ทำไมต้องทั้งสอง? — แต่ละวิธีมีข้อจำกัด: file-based ข้ามเครื่องไม่ได้, UDP multicast อาจถูกบล็อคด้วย network policy การใช้ทั้งสองวิธีทำให้ discovery ทำงานได้ในทุกสภาพแวดล้อม โดยไม่ต้องให้ผู้ใช้ configure อะไรเลย
Lease-based Task Claiming
เมื่อมีหลาย worker (หรือหลาย daemon process) วิ่งพร้อมกัน จะป้องกันไม่ให้ task ถูกทำซ้ำได้อย่างไร?
- Atomic lease — ก่อนเริ่ม task, worker ต้องขอ lease ด้วย
leaseTask(id, agentId)ซึ่งเช็คว่า task ยังไม่มีใครจอง และเขียน lease owner + expiry timestamp ลงไฟล์แบบ atomic (read-modify-write ภายใต้ lock) - Lease expiry — ถ้า worker crash หรือ disconnect, lease จะหมดอายุเอง (default 30 นาที) ทำให้ worker อื่นมารับงานต่อได้ — crash recovery โดยไม่ต้องมี distributed coordinator
- Stale lease cleanup — ตอน startLoop จะรอ 2 วินาที แล้วเรียก
expireLeases()เพื่อ clear lease เก่าจาก session ก่อนหน้า - ทำไมไม่ใช้ lock file? — lock file แบบ OS-level (flock) ใช้ข้าม process บนเครื่องเดียวกันได้ แต่ใช้ข้ามเครื่องไม่ได้ Lease บนไฟล์ JSON แก้ปัญหานี้ได้โดยไม่ต้องพึ่ง external service อย่าง Redis/ZooKeeper
Heartbeat + Stale Eviction
Peer discovery ต้องรู้ว่าใครยัง alive อยู่ โดยไม่ต้อง polling:
- Push-based heartbeat — แต่ละ peer ส่ง beacon ทุก 30 วินาที ผ่านทั้ง file (เขียน timestamp ใหม่) และ UDP multicast
- Pull-based eviction — consumer เช็คอายุของ peer entry ถ้าเกิน 90 วินาที (
PEER_STALE_TIMEOUT) ถือว่า stale → evict - 3× heartbeat interval — timeout = 3 × heartbeat interval เป็นค่า default ในระบบ distributed systems (ให้โอกาส retry ก่อน宣布ตาย)
- Graceful shutdown — ตอน
/peer stopจะส่ง UDP "offline" message + ลบไฟล์ peer ทันที ไม่ต้องรอ timeout
Random Port Binding
PeerServer bind ที่ port 0 (OS เลือก port ว่างให้):
- No port conflicts — ถ้าใช้ fixed port แล้วมีหลาย instance จะชนกัน
- Security through obscurity — port สุ่มทำให้ attacker สแกนหายากกว่า fixed port (แต่ไม่ใช่ security หลัก — security จริงอยู่ที่ authentication layer)
- Discovery solves discovery — ไม่ต้องรู้ port ล่วงหน้า เพราะ discovery protocol จะบอก port ให้เอง
Message Broadcasting
peer_broadcast ส่ง task ไปทุก peer ที่เชื่อมต่อพร้อมกัน:
- ไม่ใช่ UDP broadcast — ใช้ HTTP POST sequential ไปทีละ peer (fire-and-forget)
- รองรับ parallel สูงสุด 8 peers ต่อ 1 batch
- เหมาะสำหรับ: deploy to all machines, run tests everywhere, health check ทุก node
องค์ประกอบหลัก
PeerServer src/peer/PeerServer.ts
HTTP server ขนาดเล็กที่เริ่มบน port สุ่ม (OS เลือกให้) เมื่อรัน /peer share แต่ละ endpoint รองรับการโต้ตอบแบบต่างๆ:
| Endpoint | Method | หน้าที่ |
|---|---|---|
/peer-info | GET | ส่งข้อมูล peer (hostname, IP, cwd, shell, platform) |
/peer-msg | POST | รับข้อความ chat จาก peer อื่น |
/peer-todo | POST | รับ task ที่ถูกมอบหมายจาก peer อื่น |
/peer-exec | POST | รันคำสั่ง shell และส่งคืน stdout/stderr |
PeerDiscovery src/peer/PeerDiscovery.ts
กลไกค้นหา 2 วิธีทำงานร่วมกัน:
- File-based — แต่ละ instance เขียนไฟล์
~/.claude/peers/{pid}.jsonเก็บข้อมูล hostname, IP, port, shell, cwd instances อื่นอ่านไฟล์เพื่อค้นหา peer ในเครื่องเดียวกัน - UDP Multicast — ส่ง heartbeat beacon ไปที่
239.255.37.37:42069ทุก 30 วินาที ตอบกลับclew-peer-queryด้วยclew-peer-infoใช้ข้ามเครื่อง - Stale eviction — Peer ที่ไม่เห็น 90 วินาที (
PEER_STALE_TIMEOUT) จะถูกลบอัตโนมัติ
PeerStore src/peer/PeerStore.ts
Singleton registry ในหน่วยความจำที่เก็บ peer ทั้งหมดที่รู้จัก (ทั้งที่ค้นพบและที่ join เอง), ข้อความ chat, todos, และ custom tags (ชื่อที่กำหนดเอง, บทบาท)
- Discovered peers — ถูกลบอัตโนมัติเมื่อ stale
- Joined connections — คงอยู่ถาวร ไม่ถูกลบอัตโนมัติ
- Tags — ชื่อที่กำหนดเองและบทบาทต่อ peer
Discovery Protocol
DiscoveryMessage =
| { type: "clew-peer-query", version: 1 } // broadcast scan
| { type: "clew-peer-info", version: 1, // heartbeat / response
id: string, hostname: string, ip: string, port: number,
cwd: string, sessionId?: string, appVersion: string,
shell?: string, platform?: string, term?: string,
status: "online" | "offline" }
UDP port: 42069
Multicast IP: 239.255.37.37
Heartbeat: ทุก 30 วินาที
Stale after: 90 วินาที
คำสั่ง
| คำสั่ง | คำอธิบาย |
|---|---|
/peer | เปิดเมนู interactive (share, join, discover, inbox) |
/peer share | เริ่มประกาศ instance นี้เป็น worker บน LAN |
/peer stop | หยุดประกาศและปิด peer server |
/peer discover | สแกนหา peers (file + UDP, timeout 3s) |
/peer join <host> <port> | เชื่อมต่อกับ remote peer ด้วย host และ port |
/peer name <ชื่อ> | กำหนดชื่อที่แสดงสำหรับตัวเอง |
/peer role <บทบาท> | กำหนดบทบาท (builder, tester, deployer, ฯลฯ) |
/peer inbox | ดูข้อความและ todos ที่รออยู่จาก peers |
/peer disconnect <peer> | ตัดการเชื่อมต่อจาก peer ที่ระบุ |
/peer todos | แสดงรายการ task ทั้งหมดที่ได้รับ |
/peer todo done <id> | ทำเครื่องหมาย task ว่าเสร็จแล้ว |
AI Tools
เครื่องมือที่ AI agent ใช้เพื่อโต้ตอบกับระบบ peer:
| Tool | คำอธิบาย |
|---|---|
peer_discover | สแกน LAN หา Clew workers |
peer_join | เชื่อมต่อกับ remote peer ผ่าน HTTP |
peer_ping | ตรวจสอบว่า peer ออนไลน์อยู่หรือไม่ (GET /peer-info) |
peer_info | ดึงข้อมูล peer อย่างละเอียด |
peer_send_message | ส่งข้อความ chat ไปที่ peer |
peer_send_task | มอบหมาย task ให้ remote worker |
peer_broadcast | ส่ง task ไปทุก peer ที่เชื่อมต่อพร้อมกัน |
peer_run | รันคำสั่ง shell บน remote worker |
peer_disconnect | ลบ peer ออกจากรายการเชื่อมต่อ |
peer_list_messages | แสดงข้อความ chat ทั้งหมดที่ได้รับ |
peer_list_roles | แสดงรายการ peers พร้อมบทบาท |
peer_list_tasks | แสดง task ทั้งหมดและสถานะ |
peer_set_name | กำหนดชื่อที่แสดงให้ peer |
peer_set_role | กำหนดบทบาทให้ peer |
peer_share | เปิด/ปิด/เช็คสถานะการแชร์ |
การเชื่อมต่อกับ DAEMON
Autonomous agent loop (src/services/autonomous/agentLoop.ts) เชื่อมต่อกับ PeerServer เพื่อรับ task จาก remote peers:
- ฟัง
/peer-todoPOST ตลอด 24/7 - จัดคิว task ที่ได้รับและประมวลผลใน background workers
- สูงสุด 3 workers พร้อมกัน; task timeout หลัง 30 นาที
- ดูผลลัพธ์ผ่าน
/peer todosและ/tasks
ไฟล์ในระบบ
| ไฟล์ | หน้าที่ |
|---|---|
src/peer/PeerServer.ts | HTTP server พร้อม endpoint /peer-info, /peer-msg, /peer-todo, /peer-exec |
src/peer/PeerDiscovery.ts | ค้นหา peer ผ่านไฟล์ + UDP multicast บน LAN |
src/peer/PeerStore.ts | Singleton registry ใน memory (peers, messages, todos, tags) |
src/peer/types.ts | Shared types และค่าคงที่ของ protocol |
src/commands/peer/ | Interactive peer menu และ CLI commands |
src/tools/Peer*Tool/ | 16 AI agent tools สำหรับ peer operations |
src/services/autonomous/agentLoop.ts | Daemon integration: รับ remote tasks ผ่าน PeerServer |