Azure Pipelines по завершении job будит OpenClaw на удалённом Mac: префетч npm/NuGet/CocoaPods, затем сводка в run. Ниже — поля сервисного хука, секрет по сырым байтам, узкий PAT, JSON-шаблон и backoff 2/4/8 с. См. hardening шлюза, GHCR-матрицу, retry.
  • Фильтр триггера слишком широкий: сервисный хук срабатывает на каждую ветку и забивает общий 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"
  1. Один и тот же PATH и NODE_BINARY в plist и в SSH.
  2. Корпоративный TLS: NODE_EXTRA_CA_CERTS и доверенный корень для исходящих REST вызовов к dev.azure.com.
  3. Отдельный каталог WORK_ROOT с квотой диска и ротацией старых клонов.
  4. Журналировать 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

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