راهنمای میزبانی شخصی OpenTag برای Agent های کدنویسی
با اجرای OpenTag روی VPS، منشن های Slack و GitHub را به Agent خود متصل کنید. این راهنما تنظیمات TLS، بررسی امضای Webhook، محدوده توکن ها و پیکربندی ایمن نسخه v0.9.0 را پوشش می دهد.
عملکرد OpenTag هنگام منشن کردن یک agent
OpenTag یک @mention در ترد Slack یا issue در GitHub را به اجرای یک coding agent روی ماشینی که مالک آن هستید، تبدیل میکند. شخصی در یک issue عبارت @opentag investigate this را کامنت میکند. یک listener رویداد پلتفرم را دریافت کرده، امضای آن را بررسی میکند، منشن را با پروژهٔ متصل تطبیق میدهد، یک coding agent را روی یک checkout محلی اجرا میکند و نتیجه را در همان ترد بازمیگرداند.
این پروژه تحت مجوز MIT منتشر شده و در amplifthq/opentag قرار دارد. تا اوت 2026، جدیدترین نسخهٔ تگشده v0.9.0 است که در 28 ژوئیه 2026 منتشر شده و به صورت یک npm package عرضه میشود. هیچ image کانتینر رسمی وجود ندارد، بنابراین آنچه پین میکنید همان نسخهٔ npm است. تمام دستورات زیر آن را پین میکنند.
این پروژه به دلیل ماهیت بخش GitHub، به جای یک پروژهٔ لپتاپی، به یک پروژهٔ VPS تبدیل میشود. GitHub رویدادهای مخزن را از طریق ارسال یک درخواست HTTP به URLای که یکبار ثبت کردهاید تحویل میدهد، بنابراین آن URL باید فردا هم در همان آدرس پاسخگو باشد.
چهار بخش متحرک
شنونده (Listener) رویدادهای پلتفرم را دریافت میکند و هر پلتفرم شنوندهٔ اختصاصی خود را دارد. شنوندهٔ GitHub یک endpoint از نوع HTTP روی پورت 3050 و در مسیر /github/webhooks است. شنوندهٔ Slack Events API روی پورت 3040 و در مسیر /slack/events قرار دارد. Slack همچنین میتواند در حالت Socket Mode اجرا شود که در آن، برنامه یک WebSocket خروجی باز میکند و به هیچ پورت ورودی نیاز ندارد.
توزیعکننده (Dispatcher) هماهنگکنندهٔ سیستم است. این بخش بهصورت پیشفرض روی پورت 3030 گوش میدهد، وضعیت اجرا را در یک فایل دیتابیس محلی که توسط OPENTAG_DATABASE_PATH تعیین شده نگه میدارد و برای هر اجرا یک مسیر حسابرسی (audit trail) ثبت میکند. هیچ موجودیتی خارج از این جعبه نباید به این پورت دسترسی داشته باشد.
اجراکننده (Runner) یک دیمون محلی است. این بخش برای دریافت کار، polling انجام میدهد، یک اجرا را تصاحب کرده، آن را در اختیار میگیرد و بهصورت پیشفرض هر 15 ثانیه در طول مدت فعال بودن اجرا، یک heartbeat ارسال میکند. این بخش هر اجرای تصاحبشدهای را که هدف پروژهٔ آن موجود نباشد یا خارج از لیست مجاز (allowlist) در پیکربندی خودش باشد، رد میکند؛ این همان بررسی امنیتی است که مانع میشود یک رویداد GitHub، ایجنت شما را به سمت مخزنی هدایت کند که هرگز آن را متصل نکردهاید.
مجری (Executor) همان ایجنت کدنویسی است. OpenTag آن را از طریق ACP (پروتکل کلاینت ایجنت) راهاندازی میکند؛ یک پروتکل JSON-RPC که از طریق ورودی و خروجی استاندارد (stdin/stdout) تبادل میشود، بنابراین ایجنت بهعنوان یک پردازش فرزند در دایرکتوری کاری که OpenTag به آن اختصاص میدهد، اجرا میشود. نامهای داخلی شامل echo، codex، claude-code، cursor، opencode، hermes و openclaw هستند. با echo شروع کنید؛ این همان مجری است که همراه با پیکربندی نمونه ارائه میشود، زیرا پیش از آنکه مدل با کد شما تعامل داشته باشد، صحت کل مسیر را اثبات میکند.
ترتیب عملیات هرگز تغییر نمیکند: رویداد پلتفرم، بررسی امضا، ثبت اجرا، تصاحب، ایجنت، پاسخ در رشته (thread).
چرا لپتاپ و تونل کافی نیستند
راهنمای راهاندازی GitHub به شما میگوید که ngrok http 3050 را اجرا کنید و میزبان تونل را در webhook مخزن قرار دهید. این روش برای ده دقیقه اول کار میکند. میزبان تونل رایگان با هر بار راهاندازی مجدد فرآیند تغییر میکند و با به خواب رفتن لپتاپ از دسترس خارج میشود. GitHub همچنان از URL قدیمی payload استفاده میکند و به تلاش برای ارسال ادامه میدهد، بنابراین زبانه Recent Deliveries در تنظیمات webhook با خطاها پر میشود در حالی که رشته (thread) همچنان ساکت میماند. هیچکس تا یک هفته متوجه این موضوع نمیشود، زیرا یک webhook که هیچ کاری انجام نمیدهد، دقیقاً شبیه به یک بات است که کسی دربارهاش حرفی نزده است.
یک VPS دو مشکلی که باعث خرابی میشوند را حل میکند. نام DNS تغییر نمیکند، بنابراین URL مربوط به payload که یکبار وارد کردهاید، معتبر باقی میماند. دستگاه به خواب نمیرود، بنابراین یک کامنت در ساعت 02:00 پاسخ دریافت میکند. ابتدا سرور را بهدرستی تنظیم کنید: ده دقیقه اول روی یک VPS جدید شامل کاربر ورود و فایروالی است که این راهنما فرض میکند.
Slack یک استثنا است. در حالت Socket Mode، اتصال به سمت بیرون برقرار میشود و نیازی به URL عمومی ندارد، بنابراین استقراری که فقط برای Slack باشد میتواند بسته بماند. GitHub چنین قابلیتی ندارد. Webhookهای مخزن از نوع HTTP ورودی هستند، که به معنای نیاز به یک endpoint عمومی، TLS (امنیت لایه انتقال) و بررسی امضا است.
میزبانی OpenTag روی Ubuntu از یک نسخه مشخص (Pinned Release)
نسخه 0.9.0 از OpenTag به Node.js 22 یا جدیدتر نیاز دارد. توزیع Ubuntu 24.04 بهصورت پیشفرض Node 18 را در مخازن خود ارائه میدهد، بنابراین آن را از 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 -vدستور node -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 opentagدستور command -v opentag باید مسیری مانند /usr/bin/opentag را نمایش دهد. تنظیم linger در لینوکس اهمیت دارد: OpenTag سرویس پسزمینه خود را از طریق systemd نصب میکند و یک سرویس کاربری بدون فعال بودن linger، بلافاصله پس از بستن نشست SSH متوقف میشود.
مراحل راهاندازی را با همان کاربر اجرا کنید.
sudo -iu opentag opentag setupمراحل راهاندازی شش مورد را میپرسد: زبان CLI، آدرس گوشدهی محلی، عامل کدنویسی (coding agent)، پروژه محلی برای کار، اعتبارنامههای پلتفرم برای ذخیرهسازی، و نحوه اجرا. آدرس گوشدهی را روی 127.0.0.1 نگه دارید، زیرا nginx عملیات TLS termination را انجام داده و درخواستها را به آن هدایت میکند، بنابراین نیازی نیست که شنوندهها از خارج در دسترس باشند. برای GitHub، همچنین مخزن را با فرمت owner/repo، اجازه باز کردن pull request، پورت webhook (بهصورت پیشفرض 3050) و توکن را میپرسد. در پایان، حالت سرویس پسزمینه (background service mode) را انتخاب کنید. اگر از قبل پیکربندی دارید و میخواهید سرویس بدون پرسش نصب شود، opentag setup --service این کار را انجام میدهد.
فایل پیکربندی در /home/opentag/.config/opentag/config.json و وضعیت زمان اجرا در /home/opentag/.local/state/opentag ذخیره میشوند. پس از اینکه مراحل راهاندازی فایل را نوشت، بررسی دستی این کلیدها توصیه میشود.
{
"runnerId": "runner_local",
"dispatcherUrl": "http://localhost:3030",
"runnerToken": "...",
"approvalMode": "ask",
"repositories": []
}از runnerToken (توکن bearer با دامنه runner) بهجای pairingToken اشتراکی قدیمی استفاده کنید. فایل پیکربندی اعتبارنامهها را بهصورت متن ساده (plain text) نگه میدارد، مگر اینکه آنها را با یک ارجاع به secret جایگزین کنید که مقدار را در زمان شروع از محیط یا فایلی روی دیسک میخواند. در هر صورت، این فایل حساسترین بخش سیستم است: مجوز آن را روی 600، مالکیت را روی opentag تنظیم کنید و هرگز آن را داخل مخزن git قرار ندهید. بحث مفصلتر در نگهداری اسرار خارج از عوامل هوش مصنوعی آمده است.
پیش از در معرض دید قرار دادن هر چیزی، نصب را بررسی کنید.
sudo -iu opentag opentag doctor
sudo -iu opentag opentag statusدستور opentag doctor دیسپچر، اتصالات (bindings)، چکاوتها و اجراکنندهها را بررسی میکند. opentag status پیکربندی و وضعیت زمان اجرا را چاپ میکند و پس از ایجاد اجراها، میتوان آن را به یک اجرای خاص محدود کرد. پیش از متصل کردن پلتفرم به این سرور، تمام مواردی که doctor گزارش میدهد را اصلاح کنید.
قرار دادن TLS در لایه جلو و باز کردن تنها دو مسیر
Nginx عملیات TLS termination را انجام داده و دقیقاً دو مسیر را فوروارد میکند. هر درخواست دیگری با خطای 404 مواجه میشود، بنابراین اسکنری که میزبان را پیدا میکند، هیچ اطلاعاتی درباره سرویسهای پشت آن به دست نمیآورد.
یک بلاک server ساده برای پورت 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 (محیط مدیریت خودکار گواهی) را پوشش میدهد. بلاک نهایی به این شکل است.
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 یک تطابق دقیق (exact match) است و proxy_pass بدون هیچ عبارتی بعد از پورت، URI اصلی را بدون تغییر عبور میدهد. = را حذف کنید تا هر مسیری زیر /github/webhooks/ نیز فوروارد شود، که این کار سطح دسترسی بیشتری از نیاز listener ایجاد میکند.
فایروال باید محدود باقی بماند.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw statusپورتهای 3030، 3040 و 3050 هرگز نباید باز شوند. تأیید کنید که آنها به جای تمام اینترفیسها، فقط روی loopback متصل (bind) شدهاند.
sudo ss -tlnpهر خط OpenTag باید به صورت 127.0.0.1:3030 یا مشابه آن باشد. خطی که 0.0.0.0:3050 را نشان میدهد به این معنی است که listener خود را به کل اینترنت عرضه کرده و تنها ufw جلوی آن را گرفته است؛ این وضعیت با یک اشتباه در تنظیمات فایروال، میتواند منجر به فعال شدن یک agent باز شود. اصول فایروال ufw توضیح میدهد که آن deny پیشفرض واقعاً چه کاری انجام میدهد.
دو بررسی، صحت درب ورودی را اثبات میکند. curl -I https://opentag.example.com/ باید 404 را از nginx برگرداند که نشاندهنده معتبر بودن گواهی و بسته بودن catch-all است. درخواستی به /slack/events یا /github/webhooks که فاقد امضا باشد، هرگز نباید کد 200 برگرداند.
تمام امضاها را تأیید کنید، زیرا URL عمومی است
هر کسی میتواند URL مربوط به payload را پیدا کند. این URL در تنظیمات مخزن شما، تاریخچه مرورگر یا اسکرینشاتی که در یک تیکت الصاق شده، وجود دارد. امضا تنها چیزی است که یک تحویل واقعی از GitHub را از درخواستی که شخصی بهصورت دستی تایپ کرده است، متمایز میکند.
GitHub هر تحویل را با webhook secret امضا کرده و نتیجه را در هدر x-hub-signature-256 ارسال میکند. OpenTag آن هدر را در برابر platforms.github.webhookSecret تأیید میکند. یادداشتهای امنسازی پروژه این قانون را مستقیماً بیان میکنند: رویدادهای منبع بدون امضا را در /github/webhooks نپذیرید. Slack هر درخواست را با SLACK_SIGNING_SECRET امضا کرده و یک timestamp در آن میگنجاند، بنابراین بدنه ضبطشده نمیتواند ساعتها بعد دوباره ارسال (replay) شود.
نادیده گرفتن این مورد ریسک کوچکی نیست. یک endpoint تأییدنشده، یک payload دستنویس issue_comment حاوی @opentag را میپذیرد و سپس OpenTag یک عامل کدنویسی را با توکن شما، در checkout شما و طبق دستورات یک غریبه اجرا میکند. پاسخ به هر رشتهای (thread) که payload جعلی نام ببرد، ارسال میشود.
OpenTag دو لایه در بالا اضافه میکند. تحویلهای منبع با delivery ID ردیابی میشوند، بنابراین تحویل مجدد همان رویداد، اجرای دومی را آغاز نمیکند. فراخوانیهای Runner کلیدهای idempotency را میپذیرند، بنابراین پخش مجدد یک مورد، بدون افزودن رویداد حسابرسی (audit event) دیگر، موفقیت را برمیگرداند.
محدودیتهای نرخ (Rate limits) قابل پیکربندی هستند و باید فعال باشند. OPENTAG_RATE_LIMIT_WINDOW_MS و OPENTAG_RATE_LIMIT_MAX_REQUESTS نرخ درخواست را محدود میکنند، OPENTAG_MAX_REQUEST_BODY_BYTES بدنه را محدود میکند و یک payload بیش از حد بزرگ با 413 request_body_too_large رد میشود. OPENTAG_RATE_LIMIT_DISABLED=true برای توسعه محلی وجود دارد و در یک سرور عمومی جایگاهی ندارد. یک قانون دیگر از همان یادداشتها: یک URL رله عمومی باید از HTTPS استفاده کند و CLI فقط برای localhost اجازه استفاده از HTTP ساده را میدهد.
این بات واقعاً به چه دامنههای دسترسی (Scope) نیاز دارد؟
در GitHub، ابزار OpenTag بهجای استفاده از GitHub App، از یک personal access token با دسترسی محدود (fine-grained) استفاده میکند. مستندات بیان میکنند که مسیر استفاده از App در برنامه است و در حال حاضر تنظیمات پیشفرض CLI نیست؛ این موضوع پیامدی دارد که کاربران اغلب از آن غافل میشوند: بات با نام کاربری انسانی که توکن را ایجاد کرده است، کامنت میگذارد. توکن را تحت حسابی ایجاد کنید که مایل هستید در تمام پاسخهای تریاژ (triage) به عنوان نویسنده دیده شود.
دامنههای دسترسی را دقیقاً مطابق راهنمای نصب محدود کنید. گزینه Only select repositories را انتخاب کرده و تنها یک مخزن را برگزینید. دسترسیهای Issues: Read and write و Pull requests: Read and write را اعطا کنید. این سطح دسترسی برای خواندن منشنها و پاسخ در رشته گفتگو کافی است.
به آنچه حذف شده دقت کنید: دسترسی نوشتن به کد (Contents). ابزار OpenTag تا زمانی که preparePullRequestBranch روی true تنظیم نشده باشد، هیچ branchای را push نمیکند و یک githubApplyToken مجزا وجود دارد تا توکنی که کد مینویسد، همان توکنی نباشد که کامنتها را ثبت میکند. این دو را از هم جدا نگه دارید و توکنِ دارای دسترسی نوشتن را تا زمانی که مسیر خواندن و کامنتگذاری برای چند هفته بهدرستی کار نکرده است، فعال نکنید.
پیکربندیای که باید از آن اجتناب کرد، توکنی با دسترسی Contents: Read and write برای All repositories است. در این حالت، هر کسی که بتواند در هر یک از آن مخازن کامنت بگذارد، میتواند عاملی (agent) را هدایت کند که دسترسی commit دارد و در لاگهای حسابرسی نیز نام مالک توکن به عنوان عامل ثبت میشود. پس از آنکه عامل اعتماد لازم را کسب کرد، دامنه دسترسی را مخزن به مخزن گسترش دهید.
در Slack، دامنههای دسترسی بات عبارتند از app_mentions:read، chat:write، reactions:write و channels:history. کانالهای خصوصی همچنین به groups:history به همراه اشتراک در رویداد message.groups نیاز دارند. حالت Socket Mode به یک توکن در سطح اپلیکیشن با connections:write نیاز دارد؛ همان توکنی که با xapp- شروع میشود. channels:history تاریخچه پیامها را در کانالهای عمومی که بات به آنها اضافه شده است میخواند، بنابراین بات را فقط به کانالهایی اضافه کنید که به آن نیاز دارید، نه در همه جا.
عیبیابی مسیر یک issue از ابتدا تا انتها
ابتدا باید webhook را تنظیم کنید. در مخزن (repository) به بخش Settings، سپس Webhooks و بعد Add webhook بروید. آدرس Payload URL برابر با https://opentag.example.com/github/webhooks، نوع محتوا (content type) برابر با application/json و secret همان مقداری است که در زمان راهاندازی ایجاد کردید. فقط گزینههای Issue comments و Pull request review comments را انتخاب کنید و سایر موارد را غیرفعال بگذارید.
به محض ذخیره، GitHub یک درخواست ping ارسال میکند. بخش 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 را انجام داده و کار را شروع میکند. پاسخ در قالب یک کامنت در همان thread مربوط به issue ارسال میشود. sudo -iu opentag opentag status وضعیت اجرا را در حین انجام نشان میدهد، بنابراین میتوانید به جای حدس زدن، روند را مشاهده کنید.
پیش از اولین اجرای واقعی، approvalMode را روی ask تنظیم کنید. در حالت ask، اجرا متوقف شده و منتظر تأیید کاربر میماند تا هرگونه تغییری در وضعیت (state) ایجاد کند. حالتهای auto و autonomous نیز وجود دارند که پس از مطالعه یک ماه از گزارشهای عملکرد (transcripts) در مخزن، گزینههای معقولی برای استفاده هستند.
در سمت 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 به جای issue، روی یک diff کامنت میگذارد: یک agent بررسی pull request خودمیزبان از همین معماری استفاده میکند که به سمت pull requestها هدایت شده است. اگر میخواهید agent در حین کار به سیستمهای داخلی شما دسترسی داشته باشد، این وظیفه سرورهای MCP روی یک VPS است. جستجوی وب قابلیت دیگری است که triage مرتباً به آن نیاز دارد و اتصال agent به نمونه SearXNG شخصی باعث میشود این جستجوها روی سختافزار تحت کنترل شما انجام شود، البته با پذیرش این نکته که یک کانال ارتباطی دیگر برای ورود متنهای خارجی به agent ایجاد شده است.
اگر عامل (agent) در مقابل همه اشتباه کند چه میشود؟
پاسخ اشتباه خواهد بود. مسئله این است که این اشتباه چه هزینهای دارد.
یک پاسخ نادرست در یک issue عمومی، کامنتی است که با نامی که تیم شما میشناسد ثبت میشود و GitHub بلافاصله پس از ارسال، آن را برای تمام مشترکین ایمیل میکند. حذف کردن کامنت، ایمیل ارسالشده را بازنمیگرداند. همین موضوع در مورد اعلانهای Slack نیز صادق است. به جای برنامهریزی برای درست بودن پاسخ در محیط خصوصی، برای اشتباه بودن آن در محیط عمومی برنامهریزی کنید.
چهار گزینه میزان خسارت را محدود میکنند و اهمیت آنها از هر پرامپتی که مینویسید بیشتر است:
- در حالت
askاجرا کنید تا عامل پیشنهاد دهد، یک انسان تأیید کند و هزینه یک طرح اشتباه تنها یک کلیک باشد. - مقدار
preparePullRequestBranchرا در حالت پیشفرض false نگه دارید تا بدترین نتیجهٔ یک اجرای ناموفق، یک کامنت اشتباه باشد، نه یک branch اشتباه. - برای شروع، یک مخزن (repository) و یک کانال را متصل کنید. runner هر اجرایی را که هدف پروژهٔ آن خارج از لیست مجاز محلی باشد رد میکند، بنابراین یک مخزن متصلنشده نمیتواند عامل را به درون خود بکشد.
- توکن کامنتگذاری را از هرگونه توکن اعمال (apply) جدا نگه دارید تا با لغو دسترسی نوشتن، قابلیت تریاژ (triage) از کار نیفتد.
Slack یک دستور /stop برای اجرایی که در مسیر اشتباه پیش میرود دارد. هر اجرا همچنین یک سابقهٔ بازرسی (audit record) باقی میگذارد که شامل منشنی است که آن را آغاز کرده و کارهایی که عامل انجام داده است؛ این همان چیزی است که بعداً برای فهمیدن علت بروز خطا مطالعه میکنید.
بخش اجتماعی به اندازهٔ پیکربندی اهمیت دارد. ربات را در کانالی قرار دهید که افراد در آن انتظار یک ماشین را دارند و میدانند که ممکن است اشتباه کند. یک پاسخ اشتباهِ متقاعدکننده در کانالی با چهل نفر که تصور میکنند یک انسان آن را بررسی کرده است، هزینهای بیش از صرفهجوییِ حاصل از تریاژ دارد. در توضیحات کانال بنویسید که چه کسی مالک ربات است و چه کسی خروجی آن را بررسی میکند.
پشتیبانگیری، ارتقا و پین کردن نسخه
همه دادهها در دو مسیر قرار دارند: /home/opentag/.config/opentag/config.json و /home/opentag/.local/state/opentag. مسیر اول حاوی اعتبارنامههای شماست و مسیر دوم تاریخچه اجرا و فایل پایگاه داده را در خود دارد. از هر دو با مجوز 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بهجای دنبال کردن @latest، نسخه را پین کنید. این نرمافزار یک عامل کدنویسی را با استفاده از یک توکن فعال روی مخزن شما اجرا میکند؛ بنابراین نسخهای که شبانه منتشر میشود، یک تغییر بررسینشده در محیط شماست. سیاست امنیتی، وصلهها را به نسخههای قدیمی منتقل نمیکند و اصلاحات فقط در جدیدترین نسخه اعمال میشوند. بنابراین پین کردن به این معناست که شما گزارش تغییرات (changelog) را میخوانید و آگاهانه ارتقا میدهید. این به معنای ماندن همیشگی روی نسخه v0.9.0 نیست. تاریخچه تا ژوئیه 2026 نشان میدهد که در هر ماه چندین نسخه منتشر میشود که دلیل خوبی برای مطالعه یادداشتهای انتشار پیش از هر ارتقا است.
FAQ
آیا برای اجرای OpenTag به VPS نیاز دارم یا لپتاپ کافی است؟
برای Slack بهتنهایی، لپتاپ کافی است؛ زیرا Socket Mode یک WebSocket خروجی باز میکند و نیازی به پورت ورودی ندارد. اما GitHub متفاوت است. Webhookهای مخزن از طریق HTTP ورودی به آدرسی که یکبار ثبت کردهاید ارسال میشوند، بنابراین آدرس باید ثابت بماند و در زمانی که شما خواب هستید نیز پاسخگو باشد. آدرس میزبان تونل در حسابهای رایگان با هر بار راهاندازی مجدد تغییر میکند و GitHub همچنان به آدرس قدیمی ارسال میکند؛ این موضوع باعث ایجاد ورودیهای ناموفق در تب Recent Deliveries مخزن و سکوت در ترد (thread) میشود. یک VPS با نام DNS ثابت و گواهی معتبر، هر دو مشکل را برطرف میکند.
OpenTag به چه دسترسیهایی در GitHub نیاز دارد؟
یک personal access token با جزئیات دقیق (fine-grained) که محدود به Only select repositories باشد، با دسترسیهای Issues: Read and write و Pull requests: Read and write. این دسترسیها برای خواندن منشنها و پاسخ دادن در ترد کافی است. دسترسی نوشتن (Write) روی کد لازم نیست، مگر اینکه preparePullRequestBranch را روی true تنظیم کنید تا OpenTag شاخهها (branches) را push کند. همچنین یک githubApplyToken جداگانه وجود دارد تا توکنِ نوشتنِ کد از توکنِ کامنتگذاری جدا بماند. از استفاده از توکن با دسترسی all-repositories و contents write خودداری کنید، زیرا هر کسی که بتواند در هر یک از آن مخازن کامنت بگذارد، میتواند عاملی (agent) را هدایت کند که قادر به commit کردن است.
چگونه اجرای روندی را که دچار مشکل شده است متوقف کنم؟
Slack برای همین منظور دستوری به نام /stop دارد. روی سرور، opentag status نشان میدهد چه چیزی در حال اجراست و opentag service stop دیمون را متوقف میکند که باعث پایان یافتن کل خط لوله (pipeline) بهجای یک اجرای خاص میشود. برای جلوگیری از نیاز به هر دو، approvalMode را روی ask تنظیم کنید تا روندها پیش از هر تغییری برای تأیید توسط یک شخص متوقف شوند، و preparePullRequestBranch را روی false بگذارید تا یک اجرای ناموفق بهجای ایجاد شاخه، یک کامنت تولید کند.
چرا webhook من خطای 502 میدهد در حالی که ترد ساکت است؟
خطای 502 از سمت nginx است، نه OpenTag، و به این معنی است که پروکسی نتوانسته به شنونده (listener) متصل شود. /var/log/nginx/error.log مقدار connect() failed (111: Connection refused) while connecting to upstream را نشان خواهد داد. یا شنونده متوقف شده است، یا روی پورتی متفاوت از آنچه در خط proxy_pass تعریف شده قرار دارد. دستور sudo ss -tlnp را اجرا کنید و تأیید کنید که چیزی روی 127.0.0.1:3050 برای GitHub و 127.0.0.1:3040 برای Slack در حال گوش دادن است، سپس opentag doctor را برای بررسی bindingها و executorها اجرا کنید.