SSD Nodes Learn 🎉 VPS от $4.99/мес
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-13

Как развернуть OpenTag для обработки упоминаний агента

Настройте OpenTag на собственном VPS для интеграции со Slack и GitHub. В статье описаны проверка подписи вебхуков, настройка TLS ingress, области токенов и безопасные параметры.

Что делает OpenTag при упоминании агента

OpenTag превращает @упоминание в ветке Slack или issue на GitHub в запуск агента программирования на вашей собственной машине. Кто-то оставляет комментарий @opentag investigate this в issue. Слушатель получает событие платформы, проверяет его подпись, сопоставляет упоминание с привязанным проектом, запускает агента программирования для локальной копии репозитория и публикует результат обратно в ту же ветку.

Проект распространяется по лицензии MIT и находится по адресу amplifthq/opentag. По состоянию на август 2026 года новейший релиз с тегом — v0.9.0, опубликованный 28 июля 2026 года; он поставляется как npm-пакет. Официальный образ контейнера отсутствует, поэтому вы фиксируете версию npm. Каждая команда ниже выполняет фиксацию версии.

Это превращает проект в задачу для VPS, а не для локального ноутбука, из-за особенностей работы с GitHub. GitHub доставляет события репозитория, выполняя HTTP-запрос по URL, который вы регистрируете один раз, поэтому этот URL должен отвечать по тому же адресу и на следующий день.

Четыре функциональных компонента

Слушатель (listener) принимает события платформы, причём у каждой платформы они свои. Слушатель GitHub представляет собой HTTP-эндпоинт на порту 3050 по пути /github/webhooks. Слушатель Slack Events API работает на порту 3040 по адресу /slack/events. Slack также может работать в режиме Socket Mode: в этом случае приложение открывает исходящее WebSocket-соединение и входящий порт не требуется вовсе.

Диспетчер (dispatcher) выполняет роль координатора. По умолчанию он ожидает подключений на порту 3030, хранит состояние выполнения в локальном файле базы данных, заданном параметром OPENTAG_DATABASE_PATH, и записывает аудит для каждого запуска. Внешние системы не должны иметь доступа к этому порту.

Исполнитель (runner) — это локальный демон. Он опрашивает систему на наличие задач, резервирует выполнение, удерживает аренду и по умолчанию отправляет сигнал heartbeat каждые 15 секунд, пока задача активна. Он отклоняет любой запрос, если целевой проект отсутствует или не входит в список разрешённых в его конфигурации. Эта проверка предотвращает ситуацию, когда событие GitHub направляет агента в репозиторий, который вы не привязывали.

Исполняющий модуль (executor) — это сам агент, работающий с кодом. OpenTag запускает его по протоколу ACP (agent client protocol) — это JSON-RPC, использующий стандартные потоки ввода и вывода. Агент работает как дочерний процесс в рабочей директории, которую предоставляет OpenTag. Встроенные имена включают echo, codex, claude-code, cursor, opencode, hermes и openclaw. Начните с echo — это исполнитель, поставляемый с примером конфигурации. Он позволяет проверить работоспособность всего пути до того, как модель получит доступ к вашему коду.

Порядок действий неизменен: событие платформы, проверка подписи, запись о запуске, резервирование, агент, ответ в потоке.

Почему ноутбука и туннеля недостаточно

Руководство по настройке GitHub предлагает выполнить ngrok http 3050 и вставить хост туннеля в вебхук репозитория. Это работает первые 10 минут. Бесплатный хост туннеля меняется при каждом перезапуске процесса и перестает существовать, когда ноутбук переходит в спящий режим. GitHub сохраняет старый URL для полезной нагрузки и продолжает попытки отправки, поэтому вкладка Recent Deliveries в настройках вебхука заполняется ошибками, а поток событий остается без ответа. Никто не замечает этого неделю, так как вебхук, который ничего не делает, выглядит точно так же, как бот, о котором никто не упоминал.

VPS решает две проблемы, которые приводят к сбоям. DNS-имя не меняется, поэтому URL для полезной нагрузки, который вы вставили один раз, остается корректным. Машина не уходит в спящий режим, поэтому комментарий, оставленный в 02:00, получит ответ. Сначала правильно настройте сервер: в первые 10 минут на новом VPS описаны создание пользователя и настройка межсетевого экрана, которые подразумеваются в этом руководстве.

Slack — исключение. В режиме Socket Mode он устанавливает исходящее соединение и не требует публичного URL, поэтому развертывание только для Slack может оставаться закрытым. У GitHub нет аналога. Вебхуки репозиториев — это входящие HTTP-запросы, что означает наличие публичной конечной точки, а значит, и необходимость TLS (transport layer security) и проверки подписи.

Самостоятельный хостинг OpenTag на Ubuntu из зафиксированного релиза

Для OpenTag v0.9.0 требуется 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 может завершиться с ошибкой сразу после запуска.

Создайте для сервиса отдельную учетную запись. Агент работает с правами этого пользователя, поэтому это не должен быть ваш личный аккаунт или 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 важна в Linux: OpenTag устанавливает фоновый сервис через systemd, и пользовательский сервис без включенного linger остановится сразу после закрытия SSH-сессии.

Запустите установку от имени этого пользователя.

sudo -iu opentag opentag setup

Процесс настройки запрашивает шесть параметров: язык CLI, локальный адрес прослушивания, агент кодирования, локальный проект для работы, учетные данные платформы для сохранения и режим запуска. Оставьте адрес прослушивания 127.0.0.1, так как Nginx выполняет TLS termination и перенаправляет запросы на него, поэтому слушатели не должны быть доступны извне. Для GitHub также запрашивается репозиторий в формате owner/repo, разрешение на создание pull requests, порт для вебхуков (по умолчанию 3050) и токен. В конце выберите режим фонового сервиса. Если конфигурация уже есть и вы хотите установить сервис без интерактивных запросов, используйте 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. Файл конфигурации хранит учетные данные в открытом виде, если вы не замените их ссылкой на секрет, который считывает значение из переменной окружения или файла на диске при запуске. В любом случае этот файл — самый чувствительный объект на сервере: установите права 600, владельца opentag и никогда не храните его в git-репозитории. Более подробные аргументы приведены в безопасное хранение секретов в AI-агентах.

Проверьте установку перед тем, как открывать доступ к сервису.

sudo -iu opentag opentag doctor
sudo -iu opentag opentag status

opentag doctor проверяет диспетчер, привязки, рабочие копии и исполнители. opentag status выводит конфигурацию и состояние среды выполнения; команду можно ограничить одним запуском, если они уже существуют. Исправьте все ошибки, о которых сообщает doctor, прежде чем подключать платформу к этому серверу.

Размещение TLS на фронтенде и открытие только двух путей

Nginx терминирует TLS и перенаправляет запросы только по двум путям. Все остальные запросы возвращают 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.com

nginx -t выводит syntax is ok и test is successful, и это единственное, что защищает от опечаток при перезагрузке, которая может привести к падению сайта. В 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 без изменений. Удалите =, иначе любой путь под /github/webhooks/ также будет перенаправлен, что увеличивает поверхность атаки больше, чем требуется слушателю.

Межсетевой экран остается настроенным на минимально необходимые порты.

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 означает, что слушатель доступен из всего интернета, и его ограничивает только ufw, что при одной ошибке в настройках firewall может привести к открытию доступа к агенту. В Основы межсетевого экрана ufw объясняется, как на самом деле работает политика default deny.

Две проверки подтверждают надежность входной точки. curl -I https://opentag.example.com/ возвращает 404 от nginx, что подтверждает валидность сертификата и закрытие catch-all. Запрос к /slack/events или /github/webhooks без подписи ни в коем случае не должен возвращать 200.

Проверяйте каждую подпись, так как URL является публичным

Любой может найти URL полезной нагрузки. Он находится в настройках вашего репозитория, в истории браузера, на скриншоте, вставленном в тикет. Подпись — это единственное, что отличает реальную доставку GitHub от запроса, который кто-то ввел вручную.

GitHub подписывает каждую доставку с помощью webhook secret и отправляет результат в заголовке x-hub-signature-256. OpenTag проверяет этот заголовок на соответствие platforms.github.webhookSecret. Заметки по обеспечению безопасности проекта прямо указывают правило: не принимайте неподписанные исходные события на /github/webhooks. Slack подписывает каждый запрос с помощью SLACK_SIGNING_SECRET и включает временную метку, поэтому перехваченное тело запроса нельзя будет воспроизвести спустя часы.

Игнорирование этого требования — серьезный риск. Непроверенная конечная точка принимает созданную вручную полезную нагрузку issue_comment, содержащую @opentag, и OpenTag запускает агент кодирования с вашим токеном, в вашем рабочем каталоге, по инструкциям постороннего лица. Ответ отправляется в ту ветку обсуждения, которую указывает поддельная полезная нагрузка.

OpenTag добавляет два уровня защиты. Доставки от источников отслеживаются по delivery ID, поэтому повторная доставка того же события не запускает второй процесс. Вызовы исполнителя (runner) принимают ключи идемпотентности, поэтому повтор одного и того же запроса возвращает успех без добавления еще одного события аудита.

Ограничения частоты запросов (rate limits) настраиваемы, и их следует использовать. OPENTAG_RATE_LIMIT_WINDOW_MS и OPENTAG_RATE_LIMIT_MAX_REQUESTS ограничивают частоту запросов, OPENTAG_MAX_REQUEST_BODY_BYTES ограничивает размер тела запроса, а слишком большая полезная нагрузка отклоняется с ошибкой 413 request_body_too_large. OPENTAG_RATE_LIMIT_DISABLED=true существует для локальной разработки, и ему не место на публичном сервере. Еще одно правило из тех же заметок: публичный URL ретранслятора должен использовать HTTPS, а CLI разрешает обычный HTTP только для localhost.

Какие области доступа (scopes) действительно нужны боту?

В GitHub OpenTag использует персональный токен с ограниченными правами (fine-grained personal access token), а не GitHub App. В документации указано, что путь с использованием App планируется, но на текущий момент не является стандартным для CLI. Это влечет за собой последствие, которое часто упускают: бот оставляет комментарии от имени того пользователя, который создал токен. Создавайте его под той учетной записью, от имени которой вы готовы видеть ответы в каждом тикете.

Ограничьте область действия токена так же строго, как указано в руководстве по настройке. Выберите Only select repositories и укажите один репозиторий. Предоставьте права Issues: Read and write и Pull requests: Read and write. Этого достаточно, чтобы прочитать упоминание и ответить в ветке обсуждения.

Обратите внимание на то, чего нет в списке: прав на запись в код. OpenTag не выполняет push веток, если параметр preparePullRequestBranch не установлен в true, а для записи кода существует отдельный параметр githubApplyToken, чтобы токен, который вносит изменения в код, отличался от токена, который пишет комментарии. Разделяйте их и не активируйте токен с правами записи, пока механизм чтения и комментирования не проработает в штатном режиме несколько недель.

Конфигурация, которой следует избегать, — это токен с правами Contents: Read and write для All repositories. Любой, кто может оставить комментарий в любом из этих репозиториев, получает возможность управлять агентом, обладающим правами на коммиты, при этом в журнале аудита будет указано, что это сделал владелец токена. Расширяйте область доступа по одному репозиторию за раз, только после того, как агент подтвердит свою надежность.

Для Slack боту требуются области доступа app_mentions:read, chat:write, reactions:write и channels:history. Для работы в приватных каналах также необходимы groups:history и подписка на событие message.groups. Для Socket Mode требуется токен уровня приложения (app-level token) с правами connections:write — тот, который начинается с xapp-. Права channels:history позволяют читать историю сообщений в публичных каналах, в которые добавлен бот, поэтому добавляйте бота только в те каналы, где он необходим, а не во все подряд.

Полный цикл обработки одной задачи

Сначала настройте вебхук. В репозитории откройте 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 не смог связаться с прослушивающим сервисом; это локальная проблема, а не ошибка 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. Диспетчер зафиксирует запуск. Исполнитель (runner) возьмет задачу в работу и начнет отправлять сигналы heartbeat. Модуль выполнения (executor) откроет рабочую копию и приступит к действиям. Ответ появится в виде комментария в той же ветке обсуждения. sudo -iu opentag opentag status отображает процесс выполнения в реальном времени, поэтому вы можете наблюдать за ним, а не гадать о статусе.

Перед первым реальным запуском установите approvalMode в значение ask. В режиме ask выполнение приостанавливается и ожидает подтверждения от пользователя перед любым изменением состояния. Также существуют режимы auto и autonomous; их целесообразно использовать позже, когда вы изучите логи работы за месяц.

На стороне Slack выполнение начинается с /bind owner/repo в канале, за которым следует упоминание. Бот также отвечает на /help, /status, /doctor, /stop и /unbind confirm. Ограничьте круг лиц, имеющих право изменять привязки, с помощью OPENTAG_SLACK_BINDING_ADMIN_USER_IDS — это список Slack ID пользователей через запятую. Привязка определяет соответствие между публичным каналом и рабочей копией на вашем сервере.

Triage — подходящий первый сценарий, так как он только читает данные, не внося изменений, а результат легко оценить. Review — следующий этап, на котором агент комментирует diff, а не задачу: агент для ревью pull request с собственным хостингом использует ту же архитектуру, но направленную на pull request. Если вы хотите, чтобы агент имел доступ к вашим внутренним системам во время работы, используйте MCP-серверы на VPS. Поиск в сети — еще одна возможность, которую часто запрашивает Triage, а подключение агента к собственному экземпляру SearXNG позволяет выполнять такие запросы на вашем оборудовании, ценой появления еще одного канала, через который текст извне попадает к агенту.

Что происходит, когда агент ошибается на глазах у всех?

Он будет ошибаться. Вопрос в том, во сколько это обойдётся.

Неверный ответ в публичном тикете — это комментарий от имени, которое узнает ваша команда, и GitHub рассылает его всем подписанным пользователям в момент публикации. Удаление комментария не отзывает письмо. То же самое касается уведомлений в Slack. Планируйте работу исходя из того, что ответ будет публично ошибочным, а не из того, что он будет верным в приватном режиме.

Четыре варианта ограничивают ущерб, и они важнее любого промпта, который вы напишете.

  • Работайте в режиме ask: агент предлагает, человек одобряет, и цена ошибки — один клик.
  • Оставьте preparePullRequestBranch со значением по умолчанию false, чтобы худшим последствием неудачного запуска был неверный комментарий, а не ошибочная ветка.
  • Для начала привяжите один репозиторий к одному каналу. Исполнитель отклоняет любой запуск, если целевой проект находится вне локального списка разрешённых, поэтому непривязанный репозиторий не сможет «затянуть» агента в себя.
  • Храните токен для комментирования отдельно от токена для применения изменений, чтобы отзыв прав на запись не приводил к отключению функции сортировки тикетов (triage).

В Slack есть команда /stop для запуска, который идёт не по плану. Каждый запуск также оставляет аудиторскую запись, содержащую упоминание, которое его инициировало, и действия агента — именно это вы будете изучать позже, чтобы понять, где именно произошла ошибка.

Социальная составляющая важна не меньше, чем конфигурация. Поместите бота в канал, где люди ожидают увидеть машину и знают, что она может ошибаться. Уверенный неверный ответ в канале из сорока человек, которые полагают, что его проверил человек, стоит дороже, чем сэкономленное на сортировке тикетов время. Укажите в описании канала, кто владеет ботом и кто проверяет его вывод.

Резервное копирование, обновления и фиксация версии

Все данные хранятся в двух путях: /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. Это ПО запускает агент для работы с кодом в вашем репозитории, используя активный токен. Таким образом, релиз, выпущенный ночью, является непроверенным изменением в коде. Политика безопасности не предусматривает бэкпортов, а исправления попадают только в самый свежий релиз. Фиксация версии означает, что вы читаете список изменений и обновляетесь осознанно. Это не означает, что нужно вечно оставаться на v0.9.0. История обновлений до июля 2026 года показывает несколько релизов в месяц, что является веским доводом в пользу чтения описания изменений перед каждым обновлением.

FAQ

Нужен ли мне VPS для запуска OpenTag или достаточно ноутбука?

Ноутбука достаточно только для Slack, так как Socket Mode открывает исходящее WebSocket-соединение и не требует входящих портов. С GitHub ситуация иная. Вебхуки репозитория доставляются по входящему HTTP-запросу на URL, который вы регистрируете один раз. Поэтому адрес должен оставаться неизменным и быть доступным, пока вы спите. Хост для туннеля из бесплатного аккаунта меняется при каждой перезагрузке, а GitHub продолжает отправлять запросы на старый адрес. Это приводит к ошибкам во вкладке Recent Deliveries репозитория и отсутствию ответов в ветке обсуждения. VPS с фиксированным DNS-именем и сертификатом устраняет обе эти проблемы.

Какие права доступа на GitHub нужны для OpenTag?

Fine-grained personal access token, ограниченный настройкой Only select repositories, с правами Issues: Read and write и Pull requests: Read and write. Этого достаточно для чтения упоминаний и ответов в ветке обсуждения. Права на запись в код не требуются, если только вы не установите preparePullRequestBranch в значение true для того, чтобы OpenTag мог отправлять ветки. Для этого предусмотрена отдельная настройка githubApplyToken, чтобы токен для записи кода был отделен от токена для комментирования. Избегайте использования токена с доступом ко всем репозиториям и правами на запись содержимого, так как любой, кто может оставить комментарий в одном из таких репозиториев, сможет управлять агентом, имеющим право на выполнение коммитов.

Как остановить выполнение, которое идет не по плану?

В Slack для этого есть команда /stop. На сервере команда opentag status показывает текущие процессы, а opentag service stop останавливает демон, что завершает весь конвейер целиком, а не только один запуск. Чтобы избежать необходимости использовать эти команды, установите approvalMode в значение ask: тогда запуски будут приостанавливаться для подтверждения человеком перед внесением любых изменений. Также оставьте preparePullRequestBranch в значении false, чтобы в случае неудачного запуска создавался комментарий, а не ветка.

Почему мой вебхук возвращает 502, а в ветке тишина?

Ошибка 502 исходит от nginx, а не от OpenTag, и означает, что прокси не может связаться с прослушивающим процессом. Команда /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 для проверки привязок и исполнителей.

#opentag#ai-agents#slack#github#webhooks#self-hosting