- Фильтр триггера слишком широкий: сервисный хук срабатывает на каждую ветку и забивает общий Mac десятками префетчей.
- Секрет проверяют после пересборки JSON: подпись или сравнение строки никогда не совпадают с тем, что реально прислал Azure DevOps.
- PAT «на всё организационное дерево»: утечка из plist даёт полный доступ к коду и релизам, хотя нужна только одна запись в сборку.
Сервисный хук Azure DevOps: поля триггера и секрет Webhook
Project Settings → Service hooks → +, событие Build completed, фильтр по определению pipeline. На шлюзе читайте resource.build.id, status, sourceBranch, repository.id, плюс subscriptionId и notificationId для идемпотентности; зафиксируйте resourceVersion в фикстурах.
Секрет хука передавайте заголовком X-ADO-Webhook-Secret; на Mac сравнивайте константным временем с ADO_WEBHOOK_SECRET или Keychain, файл прав 0600. PAT не кладите в URL. HTTPS-реле → 127.0.0.1 и порт из openclaw doctor.
| Поле / настройка | Зачем на шлюзе |
|---|---|
| Фильтр pipeline / ветка | Снижает шум: префетч только для release-веток или основного YAML. |
resource.build.status |
Можно префетчить только succeeded или наоборот заранее готовить кеш при partiallySucceeded по политике команды. |
notificationId |
Ключ идемпотентности при повторной доставке того же события сервисным хуком. |
| Заголовок секрета | Быстрая ветвь до тяжёлого JSON.parse; при ошибке отвечайте 401 без тела. |
Развёртывание шлюза 2026: Node, порт OpenClaw и проверки
Node 22 LTS или 24 LTS одинаково в SSH и в plist launchd; openclaw doctor, при поддержке дистрибутива openclaw onboard --install-daemon. Порт и bind смотрите в выводе doctor или конфиге: обычно 127.0.0.1 и внутренний порт, не копируйте чужое число без сверки.
Снаружи только узкий путь вроде /hooks/ado/v1; health из LaunchAgent тем же пользователем, что и шлюз.
openclaw doctor
# пример локальной пробы (подставьте порт из doctor / конфига):
curl -fsS --connect-timeout 3 --max-time 8 \
-H "Authorization: Bearer ${OPENCLAW_DIAG_TOKEN}" \
"http://127.0.0.1:${OPENCLAW_PORT}/health"
- Один и тот же
PATHиNODE_BINARYв plist и в SSH. - Корпоративный TLS:
NODE_EXTRA_CA_CERTSи доверенный корень для исходящих REST вызовов к dev.azure.com. - Отдельный каталог
WORK_ROOTс квотой диска и ротацией старых клонов. - Журналировать
build_idиnotificationId, не секрет и не полный JSON.
Префетч зависимостей: команды и чеклист приёмки
После секрета — shallow git fetch или git clone --depth 1 в WORK_ROOT. Node: NPM_CONFIG_CACHE и npm ci --prefer-offline --no-audit --no-fund или pnpm fetch. .NET: dotnet restore без полной build. iOS: pod install --deployment при выровненных зеркалах specs.
Ограничьте параллелизм и время; при малом диске завершайте с disk_low в сводке.
| Критерий приёмки | Как проверить |
|---|---|
| Секрет | Повторная доставка с неверным заголовком даёт стабильный 401 за миллисекунды. |
| Префетч | Повтор того же build_id не создаёт второй полный restore, если идемпотентность включена. |
| Сводка | В веб-интерфейсе сборки виден тег или комментарий с JSON из шаблона ниже. |
| Нагрузка | p95 длительности обработчика укладывается в целевой бюджет, а агент pipeline не упирается в исчерпание диска. |
Сводка в сборку: шаблон JSON, PAT и backoff
Обратно — REST: тег к buildId или короткий комментарий к run. PAT: по возможности Build (Read) плюс запись через идентичность pipeline; если без PAT никак — Build (Read and execute) только на один проект. Keychain или файл chmod 600, ротация вместе с секретом хука.
Шаблон тела сводки:
{
"source": "openclaw-gateway",
"ado_project": "Contoso",
"build_id": 123456,
"prefetch": { "ok": true, "npm": "cached", "dotnet": "cached", "duration_ms": 18234 },
"log_tail": "…до 600 символов стандартного вывода…",
"host": "mac-pool-04"
}
Исходящий curl: паузы 2/4/8 с, до трёх попыток на сеть и 5xx; при 429 — Retry-After. Всегда --connect-timeout и --max-time.
ADO_URL="https://dev.azure.com/org/project/_apis/build/builds/${BUILD_ID}/tags/${TAG_NAME}?api-version=7.1"
for i in 1 2 3 4; do
code=$(curl -sS -o /tmp/ado.out -w "%{http_code}" --connect-timeout 5 --max-time 35 \
-u ":${ADO_PAT}" -X PUT -H "Content-Type: application/json" \
-d '' "$ADO_URL") || code=000
[[ "$code" =~ ^2 ]] && break
sleep $((2 ** (i-1)))
done
SLO: синхронная часть обработчика сервисного хука укладывается в несколько секунд (валидация + постановка задачи), тяжёлый префетч уходит в фон; исходящая сводка не больше нескольких килобайт.
FAQ: Azure DevOps, OpenClaw и удалённый Mac
Сервисный хук показывает 502, хотя curl с Mac на health успешен
TLS/SNI или реле режет заголовок секрета; проверьте с jump-хоста с тем же Host.
Префетч ломает pod из-за версии Ruby
Выровняйте Ruby и CocoaPods с основным агентом (launchd).
Нужно ли отвечать Azure DevOps 202 до завершения префетча
Быстрый 200/202 после проверки секрета; тяжёлое — в фон, иначе ретраи Azure DevOps.
Как объяснить безопасность без простыни для аудита
Секрет на вход, узкий PAT, allowlist исходящих, без полного JSON в центральных логах, ключ notificationId.
Итог: узкий сервисный хук Azure DevOps, сырой секрет, префетч в изолированном WORK_ROOT и компактная REST-сводка с backoff делают удалённый Mac предсказуемым узлом без лишних привилегий. Дальше: главная, покупка, центр помощи, каталог блога.
Mac под OpenClaw и Azure DevOps 24/7
Стабильный узел для сервисных хуков и префетча: центр помощи, тарифы, аренда, главная и блог.