วิธีติดตั้ง OpenTag บน VPS เพื่อรัน Coding Agent
เรียนรู้วิธีติดตั้ง OpenTag บน VPS เพื่อรับ @mention จาก Slack และ GitHub ครบถ้วนทั้งการตั้งค่า TLS ingress การตรวจสอบลายเซ็น webhook และการจัดการ token scopes อย่างปลอดภัย
การทำงานของ OpenTag เมื่อคุณกล่าวถึงเอเจนต์
OpenTag จะเปลี่ยนการ @mention ในเธรดของ Slack หรือ issue ของ GitHub ให้กลายเป็นการรัน coding agent บนเครื่องที่คุณเป็นเจ้าของ เมื่อมีคนคอมเมนต์ @opentag investigate this ลงใน issue ตัว listener จะได้รับ event จากแพลตฟอร์ม ตรวจสอบลายเซ็น จับคู่การ mention เข้ากับโปรเจกต์ที่ผูกไว้ เริ่มการทำงานของ coding agent กับโค้ดที่ checkout ไว้ในเครื่อง และโพสต์ผลลัพธ์กลับไปยังเธรดเดิม
โปรเจกต์นี้ใช้สัญญาอนุญาตแบบ MIT และอยู่ที่ amplifthq/opentag ณ เดือนสิงหาคม 2026 รุ่นล่าสุดที่มีการแท็กคือ v0.9.0 ซึ่งเผยแพร่เมื่อวันที่ 28 กรกฎาคม 2026 โดยจัดส่งในรูปแบบ npm package ปัจจุบันยังไม่มี container image อย่างเป็นทางการ ดังนั้นสิ่งที่คุณต้องระบุเวอร์ชันคือตัว npm package ทุกคำสั่งด้านล่างนี้จะทำการล็อกเวอร์ชันไว้
โปรเจกต์นี้จึงเหมาะกับการรันบน VPS มากกว่าแล็ปท็อปเนื่องจากปัจจัยด้าน GitHub โดย GitHub จะส่ง event ของ repository ผ่านการทำ HTTP request ไปยัง URL ที่คุณลงทะเบียนไว้เพียงครั้งเดียว ดังนั้น URL ดังกล่าวจึงต้องสามารถตอบสนองที่ที่อยู่เดิมได้เสมอในวันถัดไป
องค์ประกอบหลักทั้งสี่ส่วน
ตัวรับฟัง (The listener) ทำหน้าที่รับเหตุการณ์จากแพลตฟอร์ม ซึ่งแต่ละแพลตฟอร์มจะมีกลไกเฉพาะตัว ตัวรับฟังของ GitHub คือ HTTP endpoint ที่พอร์ต 3050 บน path /github/webhooks ส่วนตัวรับฟังของ Slack Events API จะอยู่ที่พอร์ต 3040 บน /slack/events นอกจากนี้ Slack ยังสามารถทำงานในโหมด Socket Mode ได้ ซึ่งแอปจะเปิดการเชื่อมต่อ WebSocket ขาออก ทำให้ไม่จำเป็นต้องเปิดพอร์ตขาเข้าเลย
ตัวกระจายงาน (The dispatcher) ทำหน้าที่เป็นผู้ประสานงาน โดยจะรับฟังที่พอร์ต 3030 เป็นค่าเริ่มต้น เก็บสถานะการทำงานไว้ในไฟล์ฐานข้อมูลภายในที่กำหนดโดย OPENTAG_DATABASE_PATH และบันทึกร่องรอยการตรวจสอบ (audit trail) สำหรับทุกการทำงาน ห้ามมิให้บริการภายนอกใดๆ เข้าถึงพอร์ตนี้โดยเด็ดขาด
ตัวรันงาน (The runner) คือ daemon ภายในเครื่อง ทำหน้าที่คอยตรวจสอบงาน (poll) เมื่อพบงานจะทำการจอง (claim) และถือครองสิทธิ์ (lease) ไว้ โดยจะส่งสัญญาณ heartbeat ทุกๆ 15 วินาทีเป็นค่าเริ่มต้นตราบเท่าที่งานยังดำเนินอยู่ ตัวรันงานจะปฏิเสธงานใดๆ ที่เป้าหมายโปรเจกต์สูญหายหรืออยู่นอกเหนือรายการที่อนุญาต (allowlist) ในไฟล์ config ของตนเอง ซึ่งเป็นการตรวจสอบเพื่อป้องกันไม่ให้เหตุการณ์จาก GitHub สั่งให้ agent ของคุณทำงานกับ repository ที่คุณไม่ได้ผูกไว้
ตัวประมวลผล (The executor) คือตัว agent ที่เขียนโค้ดขึ้นมาเอง OpenTag จะเรียกใช้งานผ่าน ACP (agent client protocol) ซึ่งเป็นโปรโตคอล JSON-RPC ที่สื่อสารผ่าน standard input และ output ดังนั้น agent จะทำงานเป็น child process ภายใน working directory ที่ OpenTag กำหนดให้ ชื่อที่ติดตั้งมาพร้อมใช้งานประกอบด้วย echo, codex, claude-code, cursor, opencode, hermes และ openclaw ให้เริ่มต้นด้วย echo ซึ่งเป็นตัวประมวลผลที่มาพร้อมกับตัวอย่าง config เพราะจะช่วยยืนยันว่าเส้นทางการทำงานทั้งหมดถูกต้องก่อนที่จะให้โมเดลเข้าถึงโค้ดของคุณ
ลำดับการทำงานไม่มีการเปลี่ยนแปลง: เหตุการณ์จากแพลตฟอร์ม, การตรวจสอบลายเซ็น, การบันทึกการทำงาน, การจองงาน, การทำงานของ agent, และการตอบกลับใน thread
เหตุผลที่แล็ปท็อปและอุโมงค์เชื่อมต่อไม่เพียงพอ
คู่มือการตั้งค่าของ GitHub แนะนำให้คุณรัน ngrok http 3050 แล้วนำ tunnel host ไปวางใน webhook ของ repository วิธีนี้ใช้งานได้เพียงสิบนาทีแรกเท่านั้น tunnel host แบบฟรีจะเปลี่ยนไปทุกครั้งที่กระบวนการทำงานใหม่ และจะหยุดทำงานเมื่อแล็ปท็อปเข้าสู่โหมดพักเครื่อง GitHub จะยังคงเก็บ payload URL เดิมไว้และพยายามส่งข้อมูลไปยังที่เดิม ส่งผลให้แท็บ Recent Deliveries ในการตั้งค่า webhook เต็มไปด้วยรายการที่ล้มเหลว ในขณะที่เธรดการทำงานยังคงเงียบสนิท ไม่มีใครสังเกตเห็นปัญหาเป็นเวลาหนึ่งสัปดาห์ เพราะ webhook ที่ไม่ทำงานจะมีลักษณะเหมือนบอทที่ไม่มีใครพูดถึง
การใช้ VPS ช่วยแก้ไขปัญหาทั้งสองประการที่ทำให้ระบบหยุดทำงาน ชื่อ DNS จะไม่เปลี่ยนแปลง ดังนั้น payload URL ที่คุณวางไว้เพียงครั้งเดียวจะยังคงถูกต้อง เครื่องจะไม่เข้าสู่โหมดพักเครื่อง ดังนั้นความคิดเห็นที่ถูกส่งเข้ามาตอน 02:00 จะได้รับคำตอบทันที โปรดตั้งค่าเซิร์ฟเวอร์ให้ถูกต้องก่อน: สิบนาทีแรกบน VPS ใหม่ ครอบคลุมถึงการตั้งค่าผู้ใช้งานและการตั้งค่า firewall ที่คู่มือนี้ถือเป็นมาตรฐาน
Slack เป็นข้อยกเว้น ในโหมด Socket Mode ระบบจะเชื่อมต่อออกไปภายนอกและไม่จำเป็นต้องมี public URL ดังนั้นการปรับใช้เฉพาะ Slack จึงสามารถปิดกั้นการเข้าถึงจากภายนอกได้ แต่ GitHub ไม่มีฟังก์ชันที่เทียบเท่ากัน Webhook ของ repository เป็นการรับข้อมูล HTTP ขาเข้า ซึ่งหมายความว่าจำเป็นต้องมี public endpoint, ต้องใช้ TLS (transport layer security) และต้องมีการตรวจสอบลายเซ็น (signature check)
การติดตั้ง OpenTag บน Ubuntu ด้วยเวอร์ชันที่กำหนด
OpenTag v0.9.0 ต้องการ Node.js 22 หรือใหม่กว่า เนื่องจาก Ubuntu 24.04 มี Node 18 ใน repository มาตรฐาน คุณจึงต้องติดตั้งผ่าน NodeSource
curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node -vnode -v จะต้องแสดงผลเป็น v22 หรือสูงกว่า หากใช้ Node 20 การติดตั้งจะแสดงคำเตือน EBADENGINE และ CLI อาจทำงานล้มเหลวหลังจากเริ่มการทำงาน
สร้างบัญชีผู้ใช้แยกต่างหากสำหรับบริการนี้ เนื่องจาก agent จะทำงานภายใต้สิทธิ์ของผู้ใช้นี้ จึงไม่ควรใช้บัญชีล็อกอินของคุณหรือ root ผู้ใช้ที่มีสิทธิ์น้อยที่สุดบน VPS อธิบายถึงเหตุผลว่าทำไมการแยกสิทธิ์เช่นนี้จึงคุ้มค่ากับขั้นตอนที่เพิ่มขึ้น
sudo adduser --disabled-password --gecos "" opentag
sudo loginctl enable-linger opentag
sudo npm install -g @opentag/cli@0.9.0
command -v opentagcommand -v opentag ควรแสดง path เช่น /usr/bin/opentag การตั้งค่า linger มีความสำคัญบน Linux เนื่องจาก OpenTag ติดตั้งบริการเบื้องหลังผ่าน systemd หากเป็นบริการของผู้ใช้ที่ไม่มีการตั้งค่า linger บริการจะหยุดทำงานทันทีที่คุณปิดเซสชัน SSH
ให้รันการตั้งค่าในฐานะผู้ใช้นั้น
sudo -iu opentag opentag setupขั้นตอนการตั้งค่าจะถามข้อมูล 6 ประการ ได้แก่ ภาษาของ CLI, ที่อยู่สำหรับการรับฟังภายในเครื่อง (local listening address), coding agent, โปรเจกต์ในเครื่องที่ต้องการทำงานด้วย, ข้อมูลยืนยันตัวตนของแพลตฟอร์มที่ต้องการบันทึก และวิธีการรัน ให้คงที่อยู่สำหรับการรับฟังไว้ที่ 127.0.0.1 เนื่องจาก nginx จะทำหน้าที่จัดการ TLS termination และส่งต่อคำขอเข้ามา ทำให้ตัวรับฟังไม่จำเป็นต้องเข้าถึงได้จากภายนอก สำหรับ GitHub ระบบจะถามถึง repository ในรูปแบบ owner/repo, สิทธิ์ในการเปิด pull request, พอร์ตสำหรับ webhook (ค่าเริ่มต้นคือ 3050) และ token ให้เลือกโหมดบริการเบื้องหลัง (background service) ในขั้นตอนสุดท้าย หากคุณมีไฟล์ config อยู่แล้วและต้องการติดตั้งบริการโดยไม่ต้องตอบคำถาม ให้ใช้ opentag setup --service
ไฟล์ config จะถูกเก็บไว้ที่ /home/opentag/.config/opentag/config.json และสถานะขณะรันไทม์จะอยู่ที่ /home/opentag/.local/state/opentag คุณควรตรวจสอบค่าเหล่านี้ด้วยตนเองหลังจากที่การตั้งค่าเขียนไฟล์เสร็จสิ้น
{
"runnerId": "runner_local",
"dispatcherUrl": "http://localhost:3030",
"runnerToken": "...",
"approvalMode": "ask",
"repositories": []
}ควรเลือกใช้ runnerToken ซึ่งเป็น bearer token แบบจำกัดขอบเขตการทำงาน (runner-scoped) แทนการใช้ pairingToken แบบแชร์ร่วมกันแบบเดิม ไฟล์ config จะเก็บข้อมูลยืนยันตัวตนเป็นข้อความธรรมดา (plain text) เว้นแต่คุณจะแทนที่ด้วยการอ้างอิง secret ซึ่งจะอ่านค่าจาก environment variable หรือจากไฟล์บนดิสก์เมื่อเริ่มทำงาน ไม่ว่าจะใช้วิธีใด ไฟล์นี้ถือเป็นสิ่งที่ละเอียดอ่อนที่สุดบนเครื่อง: ต้องตั้งค่าโหมด 600, เป็นเจ้าของโดย opentag และห้ามเก็บไว้ใน git repository โดยเด็ดขาด ข้อโต้แย้งในวงกว้างสามารถอ่านได้ที่ การเก็บความลับให้ห่างจาก AI agents
ตรวจสอบการติดตั้งก่อนที่จะเปิดเผยบริการใดๆ ออกไป
sudo -iu opentag opentag doctor
sudo -iu opentag opentag statusopentag doctor ใช้สำหรับตรวจสอบ dispatcher, bindings, checkouts และ executors ส่วน opentag status จะแสดงไฟล์ config และสถานะขณะรันไทม์ โดยสามารถจำกัดขอบเขตให้แสดงเฉพาะการรันครั้งเดียวได้เมื่อมีการรันเกิดขึ้นแล้ว ให้แก้ไขทุกอย่างที่ doctor รายงานก่อนที่จะเชื่อมต่อแพลตฟอร์มเข้ากับเครื่องนี้
วาง TLS ไว้ด้านหน้าและเปิดเพียงสอง path
nginx ทำหน้าที่ยุติ TLS และส่งต่อเพียงสอง path เท่านั้น ส่วนคำขออื่นทั้งหมดจะได้รับสถานะ 404 เพื่อไม่ให้สแกนเนอร์ที่พบโฮสต์ทราบว่ามีบริการใดทำงานอยู่เบื้องหลัง
เขียน server block สำหรับพอร์ต 80 แบบปกติไว้ที่ /etc/nginx/sites-available/opentag โดยระบุสอง location ด้านล่างนี้ จากนั้นให้ Certbot เพิ่มส่วน TLS เข้าไป
sudo apt install -y nginx certbot python3-certbot-nginx
sudo ln -s /etc/nginx/sites-available/opentag /etc/nginx/sites-enabled/opentag
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d opentag.example.comnginx -t จะแสดงผล syntax is ok และ test is successful ซึ่งเป็นสิ่งเดียวที่ป้องกันไม่ให้การพิมพ์ผิดนำไปสู่การ reload ที่ทำให้เว็บไซต์ล่ม Certbot บน Ubuntu 24.04 ด้วย nginx ครอบคลุมเรื่องการต่ออายุและสาเหตุที่ ACME (automatic certificate management environment) challenge ล้มเหลว บล็อกที่เสร็จสมบูรณ์จะมีลักษณะดังนี้
server {
listen 443 ssl;
server_name opentag.example.com;
ssl_certificate /etc/letsencrypt/live/opentag.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/opentag.example.com/privkey.pem;
client_max_body_size 2m;
location = /github/webhooks {
proxy_pass http://127.0.0.1:3050;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location = /slack/events {
proxy_pass http://127.0.0.1:3040;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location / {
return 404;
}
}= ใน location = /github/webhooks คือการจับคู่แบบตรงตัว และ proxy_pass ที่ไม่มีอะไรต่อท้ายพอร์ตจะส่งผ่าน URI เดิมไปโดยไม่มีการเปลี่ยนแปลง หากตัด = ออก ทุก path ภายใต้ /github/webhooks/ จะถูกส่งต่อด้วย ซึ่งเป็นการเพิ่มพื้นที่เสี่ยงเกินความจำเป็นสำหรับ listener นี้
ไฟร์วอลล์ยังคงถูกจำกัดไว้อย่างเข้มงวด
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw statusพอร์ต 3030, 3040 และ 3050 จะไม่ถูกเปิดใช้งาน ตรวจสอบให้แน่ใจว่าพอร์ตเหล่านี้ผูกอยู่กับ loopback แทนที่จะเป็นทุกอินเทอร์เฟซ
sudo ss -tlnpบรรทัด OpenTag ทุกบรรทัดควรแสดงเป็น 127.0.0.1:3030 หรือใกล้เคียงกัน หากบรรทัดใดแสดงเป็น 0.0.0.0:3050 หมายความว่า listener กำลังเปิดรับการเชื่อมต่อจากอินเทอร์เน็ตทั้งหมด และมีเพียง ufw เท่านั้นที่คอยกั้นไว้ ซึ่งหากตั้งค่าไฟร์วอลล์ผิดพลาดเพียงจุดเดียวอาจนำไปสู่การเปิดช่องโหว่ได้ พื้นฐานไฟร์วอลล์ ufw อธิบายว่าการตั้งค่า default deny นั้นทำงานอย่างไร
การตรวจสอบสองขั้นตอนจะยืนยันความปลอดภัยของประตูหน้า curl -I https://opentag.example.com/ จะส่งคืน 404 จาก nginx ซึ่งแสดงว่าใบรับรองถูกต้องและ catch-all ถูกปิดไว้แล้ว คำขอไปยัง /slack/events หรือ /github/webhooks ที่ไม่มีลายเซ็นจะต้องไม่ส่งคืนสถานะ 200 เป็นอันขาด
ตรวจสอบทุกลายเซ็น เนื่องจาก URL เป็นแบบสาธารณะ
ทุกคนสามารถเข้าถึง URL ของ payload ได้ ไม่ว่าจะจากหน้าการตั้งค่า repository, ประวัติการเข้าชมเว็บ หรือภาพหน้าจอที่แปะไว้ในตั๋วงาน ลายเซ็นดิจิทัลเป็นสิ่งเดียวที่แยกแยะระหว่างการส่งข้อมูลจริงจาก GitHub กับคำขอที่ใครบางคนพิมพ์ขึ้นมาเอง
GitHub จะลงลายเซ็นในทุกการส่งข้อมูลด้วย webhook secret และส่งผลลัพธ์มาใน header x-hub-signature-256 โดย OpenTag จะตรวจสอบ header นั้นเทียบกับ platforms.github.webhookSecret บันทึกการเพิ่มความปลอดภัยของโปรเจกต์ระบุไว้ชัดเจนว่า ห้ามยอมรับเหตุการณ์จากแหล่งที่มาที่ไม่มีลายเซ็นบน /github/webhooks ส่วน Slack จะลงลายเซ็นในทุกคำขอด้วย SLACK_SIGNING_SECRET และระบุ timestamp ไว้ด้วย เพื่อป้องกันไม่ให้ข้อมูลที่ถูกดักจับถูกนำมาส่งซ้ำ (replay) ในภายหลัง
การข้ามขั้นตอนการตรวจสอบนี้ไม่ใช่ความเสี่ยงเล็กน้อย endpoint ที่ไม่มีการตรวจสอบจะยอมรับ payload issue_comment ที่ถูกสร้างขึ้นเองซึ่งมี @opentag อยู่ภายใน จากนั้น OpenTag จะรัน coding agent โดยใช้ token ของคุณ ใน checkout ของคุณ ตามคำสั่งของคนแปลกหน้า และส่งผลลัพธ์กลับไปยัง thread ใดก็ตามที่ payload ปลอมนั้นระบุไว้
OpenTag เพิ่มการป้องกันอีกสองชั้น การส่งข้อมูลจากแหล่งที่มาจะถูกติดตามด้วย delivery ID ดังนั้นการส่งเหตุการณ์เดิมซ้ำจะไม่ทำให้เกิดการรันงานรอบที่สอง ส่วนการเรียกใช้งาน runner จะรองรับ idempotency keys ทำให้การส่งซ้ำได้รับสถานะสำเร็จโดยไม่เพิ่ม audit event รายการใหม่เข้าไป
อัตราการจำกัดคำขอ (rate limits) สามารถกำหนดค่าได้และควรเปิดใช้งานไว้ โดย OPENTAG_RATE_LIMIT_WINDOW_MS และ OPENTAG_RATE_LIMIT_MAX_REQUESTS จะเป็นตัวกำหนดขอบเขตอัตราคำขอ OPENTAG_MAX_REQUEST_BODY_BYTES กำหนดขนาดของ body และหาก payload มีขนาดใหญ่เกินไปจะถูกปฏิเสธด้วย 413 request_body_too_large ส่วน OPENTAG_RATE_LIMIT_DISABLED=true มีไว้สำหรับการพัฒนาในเครื่องเท่านั้น และไม่ควรนำไปใช้บนเซิร์ฟเวอร์สาธารณะ กฎอีกข้อจากบันทึกเดียวกันคือ URL ของ relay สาธารณะต้องใช้ HTTPS เท่านั้น โดย CLI จะอนุญาตให้ใช้ HTTP แบบธรรมดาได้เฉพาะกับ localhost เท่านั้น
บอทจำเป็นต้องใช้ token scope อะไรบ้าง
บน GitHub นั้น OpenTag ใช้ fine-grained personal access token แทนที่จะเป็น GitHub App เอกสารระบุว่าเส้นทางสำหรับ App นั้นอยู่ในแผนงานและไม่ใช่ค่าเริ่มต้นของการตั้งค่า CLI ในปัจจุบัน ซึ่งส่งผลที่หลายคนมองข้ามไปคือ บอทจะแสดงความคิดเห็นในนามของบุคคลที่เป็นเจ้าของ token นั้น ดังนั้นควรสร้าง token ภายใต้บัญชีที่คุณยอมรับได้หากชื่อบัญชีนั้นจะปรากฏในทุกการตอบกลับเพื่อคัดกรองปัญหา
กำหนดขอบเขต (scope) ให้จำกัดที่สุดตามคู่มือการตั้งค่า เลือก Only select repositories แล้วระบุเพียงที่เก็บเดียว ให้สิทธิ์ Issues: Read and write และ Pull requests: Read and write เพียงเท่านี้ก็เพียงพอสำหรับการอ่านการกล่าวถึง (mention) และตอบกลับในกระทู้แล้ว
สังเกตสิ่งที่ขาดไปคือสิทธิ์การเขียนโค้ด (write access to code) OpenTag จะไม่ push branch เว้นแต่ preparePullRequestBranch จะถูกตั้งค่าเป็น true และมี githubApplyToken แยกต่างหาก เพื่อให้ token ที่ใช้เขียนโค้ดไม่ใช่ token เดียวกับที่ใช้เขียนความคิดเห็น ควรแยก token เหล่านี้ออกจากกัน และเก็บ token ที่มีสิทธิ์เขียนโค้ดไว้ก่อนจนกว่าเส้นทางการอ่านและตอบกลับจะทำงานได้อย่างราบรื่นเป็นเวลาสองสามสัปดาห์
การตั้งค่าที่ควรหลีกเลี่ยงคือการใช้ token ที่มีสิทธิ์ Contents: Read and write ครอบคลุม All repositories เพราะทุกคนที่สามารถแสดงความคิดเห็นในที่เก็บเหล่านั้นจะสามารถควบคุมเอเจนต์ที่มีสิทธิ์ commit ได้ และบันทึกการตรวจสอบ (audit trail) จะระบุว่าเจ้าของ token เป็นผู้กระทำ ควรขยายขอบเขตทีละที่เก็บหลังจากที่เอเจนต์ได้รับความไว้วางใจแล้วเท่านั้น
บน Slack ขอบเขตของบอทคือ app_mentions:read, chat:write, reactions:write และ channels:history สำหรับช่องทางส่วนตัว (private channels) จำเป็นต้องใช้ groups:history เพิ่มเติมพร้อมกับการสมัครรับข้อมูลเหตุการณ์ message.groups ส่วน Socket Mode จำเป็นต้องใช้ app-level token ที่มีสิทธิ์ connections:write ซึ่งเป็น token ที่ขึ้นต้นด้วย xapp- สำหรับ channels:history นั้นใช้สำหรับอ่านประวัติข้อความในช่องทางสาธารณะที่บอทถูกเพิ่มเข้าไป ดังนั้นควรเพิ่มบอทเฉพาะในช่องทางที่ต้องการใช้งานเท่านั้น แทนที่จะเพิ่มในทุกช่องทาง
การตรวจสอบปัญหาเส้นทางจากต้นทางถึงปลายทาง
Webhook คือจุดเริ่มต้น ให้ไปที่ Settings ใน repository จากนั้นเลือก Webhooks แล้วกด Add webhook โดยกำหนด Payload URL เป็น https://opentag.example.com/github/webhooks, Content type เป็น application/json และใช้ Secret ที่ได้จากการตั้งค่าเริ่มต้น เลือกสมัครรับข้อมูลเฉพาะ Issue comments และ Pull request review comments เท่านั้น
GitHub จะส่ง Ping delivery ทันทีที่คุณบันทึก ให้เปิดดู Recent Deliveries เพื่อตรวจสอบว่าคำขอมาถึงเซิร์ฟเวอร์หรือไม่ หากพบสถานะ 502 หมายความว่า nginx ไม่สามารถติดต่อ listener ได้ ซึ่งเป็นปัญหาภายในเครื่อง ไม่ใช่ปัญหาจากฝั่ง GitHub
จากนั้นให้ทดสอบใช้งาน โดยเปิด issue ที่แจ้งบั๊กแล้วพิมพ์คอมเมนต์ดังนี้:
@opentag triage this. Reproduce the report against the current main branch, then reply with the file and function most likely responsible, plus the test you would write first.สิ่งที่ควรเกิดขึ้นตามลำดับคือ Recent Deliveries จะบันทึกการส่ง issue_comment ด้วยสถานะตอบกลับ 2xx, Dispatcher จะบันทึกการทำงาน (run), Runner จะรับงานและเริ่มส่งสัญญาณ heartbeat, Executor จะเปิด checkout และเริ่มประมวลผล จากนั้นคำตอบจะปรากฏเป็นคอมเมนต์ในกระทู้ issue เดียวกัน คุณสามารถใช้ sudo -iu opentag opentag status เพื่อดูสถานะการทำงานขณะที่กำลังประมวลผล แทนการคาดเดา
ตั้งค่า approvalMode เป็น ask ก่อนการรันงานจริงครั้งแรก ในโหมด ask การทำงานจะหยุดพักและรอการยืนยันจากผู้ใช้ก่อนที่จะดำเนินการใดๆ ที่ส่งผลต่อสถานะ นอกจากนี้ยังมีโหมด auto และ autonomous ซึ่งเหมาะสมที่จะใช้งานในภายหลัง เมื่อคุณได้อ่านบันทึกการทำงานมาแล้วเป็นเวลาหนึ่งเดือน
ในฝั่ง Slack การทำงานเดียวกันจะเริ่มด้วย /bind owner/repo ในแชนเนล ตามด้วยการกล่าวถึง (mention) บอทจะตอบกลับด้วย /help, /status, /doctor, /stop และ /unbind confirm ให้จำกัดสิทธิ์ผู้ที่สามารถเปลี่ยนการเชื่อมโยง (binding) ได้ด้วย OPENTAG_SLACK_BINDING_ADMIN_USER_IDS ซึ่งเป็นรายการ Slack user ID คั่นด้วยคอมมา เนื่องจาก binding คือการจับคู่ระหว่างแชนเนลสาธารณะกับ checkout บนเซิร์ฟเวอร์ของคุณ
Triage เป็นเส้นทางเริ่มต้นที่ดีเพราะเป็นการอ่านข้อมูลโดยไม่มีการเขียน และประเมินผลคำตอบได้ง่าย ขั้นตอนถัดไปคือ Review ซึ่งตัวแทน (agent) จะคอมเมนต์บน diff แทนที่จะเป็น issue: ตัวแทนตรวจสอบ pull request แบบ self-hosted คือสถาปัตยกรรมเดียวกันนี้ที่ปรับใช้กับ pull request หากคุณต้องการให้ตัวแทนเข้าถึงระบบของคุณเองในระหว่างทำงาน นั่นเป็นหน้าที่ของ MCP servers บน VPS การค้นหาข้อมูลผ่านเว็บเป็นอีกความสามารถที่ Triage มักเรียกใช้ และ การเชื่อมต่อตัวแทนเข้ากับ SearXNG instance ของคุณเอง จะช่วยให้การสืบค้นเหล่านั้นอยู่บนฮาร์ดแวร์ที่คุณดูแลเอง โดยแลกกับช่องทางเพิ่มเติมที่ข้อความจากบุคคลภายนอกจะส่งถึงตัวแทนได้
จะเกิดอะไรขึ้นเมื่อเอเจนต์ให้ข้อมูลผิดพลาดต่อหน้าทุกคน?
มันย่อมต้องผิดพลาด คำถามคือความผิดพลาดนั้นมีราคาที่ต้องจ่ายเท่าใด
การตอบกลับที่ผิดพลาดในประเด็นสาธารณะคือความคิดเห็นภายใต้ชื่อที่ทีมของคุณรู้จัก และ GitHub จะส่งอีเมลแจ้งเตือนไปยังทุกคนที่ติดตามทันทีที่โพสต์ การลบความคิดเห็นไม่สามารถเรียกคืนอีเมลนั้นได้ เช่นเดียวกับการแจ้งเตือนใน Slack จงวางแผนรับมือกรณีที่คำตอบผิดพลาดในที่สาธารณะ แทนที่จะคาดหวังว่ามันจะถูกต้องในที่ลับ
ทางเลือก 4 ประการต่อไปนี้จะช่วยจำกัดความเสียหาย ซึ่งมีความสำคัญมากกว่า prompt ใดๆ ที่คุณเขียน:
- รันในโหมด
askเพื่อให้เอเจนต์เป็นผู้เสนอ และให้บุคคลเป็นผู้อนุมัติ ดังนั้นแผนที่ผิดพลาดจะมีราคาที่ต้องจ่ายเพียงแค่การคลิกหนึ่งครั้ง - คงค่า
preparePullRequestBranchไว้ที่ค่าเริ่มต้นคือ false เพื่อให้ผลลัพธ์ที่แย่ที่สุดของการรันที่ผิดพลาดเป็นเพียงความคิดเห็นที่ผิด แทนที่จะเป็น branch ที่ผิด - ผูกหนึ่ง repository เข้ากับหนึ่งช่องทางเพื่อเริ่มต้นใช้งาน ตัว runner จะปฏิเสธการรันใดๆ ที่เป้าหมายโปรเจกต์อยู่นอกเหนือรายการที่อนุญาต (allowlist) ดังนั้น repository ที่ไม่ได้ผูกไว้จะไม่สามารถดึงเอเจนต์เข้าไปทำงานได้
- แยก token สำหรับการแสดงความคิดเห็นออกจาก token สำหรับการปรับใช้ (apply) เพื่อให้การเพิกถอนสิทธิ์การเขียนไม่ส่งผลกระทบต่อการคัดกรองงาน (triage)
Slack มีคำสั่ง /stop สำหรับการรันที่กำลังดำเนินไปในทิศทางที่ผิด ทุกการรันจะทิ้งบันทึกการตรวจสอบ (audit record) ที่เก็บข้อมูลการกล่าวถึง (mention) ที่เป็นจุดเริ่มต้นและสิ่งที่เอเจนต์ได้ทำลงไป ซึ่งเป็นสิ่งที่คุณต้องอ่านในภายหลังเพื่อหาสาเหตุว่ามันผิดพลาดที่จุดใด
แง่มุมทางสังคมมีความสำคัญไม่น้อยไปกว่าการตั้งค่าคอนฟิก จงวางบอทไว้ในช่องทางที่ผู้คนคาดหวังว่าจะเจอเครื่องจักรและทราบดีว่ามันสามารถผิดพลาดได้ คำตอบที่ผิดพลาดอย่างมั่นใจในช่องทางที่มีคนสี่สิบคนที่เข้าใจผิดว่ามีมนุษย์ตรวจสอบแล้วนั้น มีราคาที่ต้องจ่ายสูงกว่าเวลาที่ประหยัดได้จากการคัดกรองงาน จงระบุไว้ในคำอธิบายช่องทางว่าใครเป็นเจ้าของบอทและใครเป็นผู้ตรวจสอบผลลัพธ์ของมัน
การสำรองข้อมูล การอัปเกรด และการตรึงเวอร์ชัน
ข้อมูลทั้งหมดจะถูกเก็บไว้ในสองพาธ คือ /home/opentag/.config/opentag/config.json และ /home/opentag/.local/state/opentag พาธแรกเก็บข้อมูลรับรอง (credentials) ของคุณ ส่วนพาธที่สองเก็บประวัติการทำงานและไฟล์ฐานข้อมูล ให้สำรองข้อมูลทั้งสองแห่งด้วยโหมด 600 และเก็บไว้ภายนอกเซิร์ฟเวอร์ การสูญเสียข้อมูลเหล่านี้หมายถึงคุณต้องสร้างโทเค็นและการเชื่อมโยงใหม่ ไม่ใช่การสร้างเซิร์ฟเวอร์ขึ้นมาใหม่
การอัปเกรดทำได้โดยการปรับเวอร์ชันและรีสตาร์ทบริการ
sudo npm install -g @opentag/cli@0.9.0
sudo -iu opentag opentag service stop
sudo -iu opentag opentag service start
sudo -iu opentag opentag doctorให้ตรึงเวอร์ชัน (pin) แทนการติดตาม @latest ซอฟต์แวร์นี้จะรัน coding agent เข้ากับ repository ของคุณโดยใช้โทเค็นที่ใช้งานจริง ดังนั้นการออกรุ่นใหม่ในช่วงข้ามคืนจึงถือเป็นการเปลี่ยนแปลงที่ยังไม่ได้ผ่านการตรวจสอบ นโยบายความปลอดภัยไม่มีการ backport การแก้ไขใดๆ และการแก้ไขจะรวมอยู่ในรุ่นล่าสุดเท่านั้น ดังนั้นการตรึงเวอร์ชันหมายความว่าคุณต้องอ่าน changelog และดำเนินการอัปเกรดด้วยความตั้งใจ ไม่ได้หมายความว่าให้ใช้ v0.9.0 ตลอดไป ประวัติการใช้งานจนถึงเดือนกรกฎาคม 2026 แสดงให้เห็นว่ามีการออกรุ่นใหม่หลายครั้งต่อเดือน ซึ่งเป็นเหตุผลที่ดีที่คุณควรตรวจสอบบันทึกประจำรุ่น (release notes) ก่อนการอัปเกรดแต่ละครั้ง
FAQ
ฉันจำเป็นต้องใช้ VPS เพื่อรัน OpenTag หรือใช้แล็ปท็อปก็เพียงพอแล้ว?
แล็ปท็อปเพียงพอสำหรับการใช้งาน Slack อย่างเดียว เพราะ Socket Mode เปิดการเชื่อมต่อ WebSocket แบบขาออกและไม่จำเป็นต้องมีพอร์ตขาเข้า แต่ GitHub นั้นต่างออกไป Webhook ของ repository จะส่งข้อมูลผ่าน HTTP ขาเข้ามายัง URL ที่คุณลงทะเบียนไว้ ดังนั้นที่อยู่ต้องคงที่และต้องพร้อมตอบสนองตลอดเวลาแม้ในขณะที่คุณไม่ได้ใช้งาน โฮสต์สำหรับทำ tunnel จากบัญชีฟรีจะเปลี่ยนที่อยู่ทุกครั้งที่รีสตาร์ท ทำให้ GitHub ยังคงส่งข้อมูลไปยังที่อยู่เดิม ซึ่งจะปรากฏเป็นรายการที่ล้มเหลวในแท็บ Recent Deliveries ของ repository และทำให้ไม่มีการตอบกลับใน thread การใช้ VPS ที่มีชื่อ DNS คงที่และมี certificate จะช่วยแก้ปัญหาทั้งสองอย่างนี้ได้
OpenTag ต้องการสิทธิ์การเข้าถึง GitHub อะไรบ้าง?
ต้องการ personal access token แบบละเอียด (fine-grained) ที่จำกัดไว้เฉพาะ Only select repositories โดยให้สิทธิ์ Issues: Read and write และ Pull requests: Read and write ซึ่งเพียงพอสำหรับการอ่านการกล่าวถึง (mention) และการตอบกลับใน thread สิทธิ์การเขียนโค้ดไม่จำเป็นเว้นแต่คุณจะตั้งค่า preparePullRequestBranch เป็น true เพื่อให้ OpenTag ทำการ push branch และมี githubApplyToken แยกต่างหากเพื่อให้ token สำหรับเขียนโค้ดแยกออกจาก token สำหรับแสดงความคิดเห็น หลีกเลี่ยงการใช้ token ที่เข้าถึงได้ทุก repository พร้อมสิทธิ์ contents write เพราะใครก็ตามที่สามารถแสดงความคิดเห็นใน repository เหล่านั้นได้ อาจควบคุม agent ที่สามารถ commit โค้ดได้
ฉันจะหยุดการทำงานที่กำลังผิดพลาดได้อย่างไร?
Slack มีคำสั่ง /stop สำหรับกรณีนี้โดยเฉพาะ บนเซิร์ฟเวอร์ คำสั่ง opentag status จะแสดงสิ่งที่กำลังทำงานอยู่ และ opentag service stop จะหยุด daemon ซึ่งจะยุติทั้ง pipeline แทนที่จะหยุดเพียงแค่การทำงานเดียว เพื่อหลีกเลี่ยงการใช้คำสั่งเหล่านี้ ให้ตั้งค่า approvalMode เป็น ask เพื่อให้การทำงานหยุดรอการยืนยันจากผู้ใช้ก่อนที่จะมีการเปลี่ยนแปลงใดๆ และคงค่า preparePullRequestBranch ไว้ที่ false เพื่อให้การทำงานที่ผิดพลาดสร้างเป็นความคิดเห็นแทนที่จะสร้าง branch
ทำไม webhook ของฉันถึงคืนค่า 502 ในขณะที่ thread ยังคงเงียบ?
ข้อผิดพลาด 502 มาจาก nginx ไม่ใช่จาก OpenTag ซึ่งหมายความว่า proxy ไม่สามารถติดต่อกับ listener ได้ คำสั่ง /var/log/nginx/error.log จะแสดง connect() failed (111: Connection refused) while connecting to upstream สาเหตุอาจเกิดจาก listener หยุดทำงาน หรือ listener อยู่บนพอร์ตที่ต่างจากที่ระบุไว้ในบรรทัด proxy_pass ให้รันคำสั่ง sudo ss -tlnp และตรวจสอบว่ามีบริการกำลังฟังอยู่ที่พอร์ต 127.0.0.1:3050 สำหรับ GitHub และ 127.0.0.1:3040 สำหรับ Slack จากนั้นรัน opentag doctor เพื่อตรวจสอบการเชื่อมต่อ (bindings) และตัวประมวลผล (executors)