#Maintenance-ключ агента

POST /api/agents/:id/maintenance-key (выдача из UI агента)

Maintenance-ключ — это временный API-ключ с TTL 3 дня, привязанный к одному серверу агента. Выдаётся через кнопку «Запросить ключ обслуживания» в карточке агента. Назначение — отдать строку vibe_live_… и инструкцию для отладки в AI-инструмент (Claude Code, Cursor, MCP-клиент): тот выполнит команды на VM агента и починит молчащего или некорректно работающего бота.

Под капотом это обычный MANAGEMENT-ключ (type=MANAGEMENT, префикс vibe_live_) с дополнительными колонками linkedServerId (UUID сервера) и purpose='agent-maintenance'. Скоуп vibe:infra присутствует автоматически. Ключ открывает доступ только к четырём эндпоинтам Deploy API из этого документа и только для одного сервераlinkedServerId. Любая попытка вызвать те же эндпоинты для другого сервера вернёт 403 WRONG_KEY.

#Когда выдавать ключ

Сценарий Выдавать?
Бот агента перестал отвечать на сообщения в чате B24 да
Бот отвечает, но логика ответа сломана (неправильная роль, не вызывает MCP-инструмент) да
Нужно проверить конфиг openclaw.json без редеплоя агента да
Хочется потрогать сервер «из любопытства» нет — нужен общий API-ключ
Регулярные административные задачи (мониторинг, метрики) нет — для них есть менеджмент-ключи с длинным TTL
Нужен доступ к нескольким серверам одного владельца нет — выдайте отдельный ключ для каждого сервера

После починки ключ нужно сразу отозвать через UI или дождаться авто-истечения через 3 дня — expiresAt устанавливается на момент выдачи. Кратко: ключ одноразовый, на конкретный инцидент.

#Анатомия VM агента

Агент исполняется как Docker-контейнер openclaw на BLACKHOLE-сервере (Ubuntu + cloud-init установка из scripts/install_openclaw.sh).

Что Где
Контейнер docker ps → имя openclaw
Конфиг агента (на хосте) /opt/openclaw/config/openclaw.json
Конфиг агента (внутри контейнера) /home/node/.openclaw/openclaw.json (volume-mount)
Политики выполнения команд /opt/openclaw/config/exec-approvals.json (YOLO-режим)
Логи контейнера docker logs openclaw
Системные логи (cloud-init, vibe-agent, контейнер) journalctl через `GET /logs`
Перезапуск контейнера docker restart openclaw
Перечитать конфиг без рестарта docker kill -s SIGUSR1 openclaw (OpenClaw умеет hot-reload)

OpenClaw читает openclaw.json при старте и при сигнале SIGUSR1. Если правите конфиг через /upload, можно либо перезапустить контейнер, либо послать SIGUSR1 — оба варианта подхватывают новый конфиг.

#Аутентификация

Передаётся заголовок X-Api-Key со значением vibe_live_<...>:

Terminal
curl -H "X-Api-Key: vibe_live_abc123..." \
  https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/logs?lines=50
Что проверяется Поведение при ошибке
Ключ существует и активен 401 KEY_INACTIVE после отзыва из UI
expiresAt > NOW() (3-дневный TTL) 401 KEY_EXPIRED после истечения
Запрашиваемый сервер совпадает с linkedServerId ключа 403 WRONG_KEY если сервер чужой
Сервер не удалён (soft-delete) 404 NOT_FOUND если сервер удалён

Maintenance-ключ не работает ни для одного эндпоинта вне списка четырёх ниже. Управление ключами (/v1/keys/*), биллинг, OAuth-проксирование сущностей — недоступны.

#Доступные эндпоинты

Метод Эндпоинт Назначение
POST /v1/infra/servers/:id/exec Произвольная shell-команда на VM
POST /v1/infra/servers/:id/upload Запись файла (с авто-распаковкой архивов)
GET /v1/infra/servers/:id/logs Журнал journalctl
POST /v1/infra/servers/:id/deploy Полный реинсталл агента (многошаговая оркестрация)

:id — UUID сервера, на котором живёт агент. Можно прочитать из UI карточки агента (поле «ID сервера») или из ответа GET /api/agents/:id поле serverId.


#Выполнить команду

POST /v1/infra/servers/:id/exec

Выполняет одну shell-команду на VM агента. Полный stdout, stderr и exitCode возвращаются клиенту. По умолчанию — синхронный JSON-ответ (рекомендуется для AI-инструментов). Опционально SSE-режим через ?stream=true.

#Поля запроса

Поле Тип Обяз. Описание
command string да Shell-команда, 1–10 000 символов
timeout number нет Таймаут в секундах, 1–600. По умолчанию 300 (5 мин)
workdir string нет Рабочая директория, до 500 символов
env object нет Переменные окружения { "KEY": "value" }, только строки

#Пример запроса — cURL

Terminal
# Прочитать последние строки лога OpenClaw-контейнера
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/exec \
  -H "X-Api-Key: vibe_live_..." \
  -H "Content-Type: application/json" \
  -d '{"command": "docker logs openclaw --tail 200", "timeout": 30}'

#Пример запроса — JavaScript

javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/exec`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'vibe_live_...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      command: 'docker restart openclaw',
      timeout: 60,
    }),
  }
)
const { data } = await res.json()
console.log(`exit ${data.exitCode} за ${data.duration}s`)
console.log(data.stdout)

#Поля ответа

Поле Тип Описание
success boolean true если команда выполнена (включая ненулевой exitCode)
data.exitCode number Код возврата процесса
data.stdout string Стандартный вывод (до 5 МБ)
data.stderr string Поток ошибок (до 5 МБ)
data.duration number Время выполнения, секунд
data.truncated boolean true если stdout/stderr обрезаны

#Пример успешного ответа

JSON
{
  "success": true,
  "data": {
    "exitCode": 0,
    "stdout": "openclaw\n",
    "stderr": "",
    "duration": 1,
    "truncated": false
  }
}

#Пример ответа при ошибке

409 — на сервере уже идёт другая операция:

JSON
{
  "success": false,
  "error": {
    "code": "EXEC_BUSY",
    "message": "Another operation is running on this server"
  }
}

504 — превышен таймаут:

JSON
{
  "success": false,
  "error": {
    "code": "EXEC_TIMEOUT",
    "message": "Command timed out after 300 seconds"
  }
}

#Загрузить файл

POST /v1/infra/servers/:id/upload

Записывает файл на VM. Источник — либо base64-содержимое в теле запроса (до 50 МБ), либо HTTPS-URL, который агент скачает сам (до 500 МБ). Поддерживает авто-распаковку tar.gz/tar.bz2/zip — формат определяется по сигнатуре файла или расширению.

#Поля запроса

Поле Тип Обяз. Описание
path string да Куда записать файл, 1–500 символов
content string да (или url) Base64-содержимое, до 50 МБ
url string да (или content) HTTPS-URL источника, до 500 МБ
mode string нет Права в восьмеричном формате: "0644", "0755"
extract boolean нет Распаковать архив после загрузки. По умолчанию false
extractTo string нет Директория для распаковки (если extract: true)

Передаётся ровно одно из content / url.

#Пример запроса — cURL

Terminal
# Залить новый openclaw.json (вариант: base64-содержимое)
CONTENT=$(base64 -i ./openclaw.json)
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/upload \
  -H "X-Api-Key: vibe_live_..." \
  -H "Content-Type: application/json" \
  -d "{
    \"content\": \"$CONTENT\",
    \"path\": \"/opt/openclaw/config/openclaw.json\",
    \"mode\": \"0644\"
  }"

#Пример запроса — JavaScript

javascript
import { readFile } from 'node:fs/promises'

const content = (await readFile('./openclaw.json')).toString('base64')

const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/upload`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'vibe_live_...',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content,
      path: '/opt/openclaw/config/openclaw.json',
      mode: '0644',
    }),
  }
)
const { data } = await res.json()
console.log(`Записано ${data.size} байт по ${data.path}`)

#Поля ответа

Поле Тип Описание
success boolean true при успешной записи
data.path string Итоговый путь файла на VM
data.size number Размер в байтах
data.extracted boolean true если архив распакован

#Пример успешного ответа

JSON
{
  "success": true,
  "data": {
    "path": "/opt/openclaw/config/openclaw.json",
    "size": 1247,
    "extracted": false
  }
}

#Пример ответа при ошибке

403 — путь запрещён (системные директории /etc/passwd, /root/.ssh, /boot блокируются):

JSON
{
  "success": false,
  "error": {
    "code": "UPLOAD_PATH_DENIED",
    "message": "Upload to system path is not allowed"
  }
}

#Логи сервера

GET /v1/infra/servers/:id/logs

Читает системный журнал journalctl на VM. Без service — весь системный журнал (cloud-init, vibe-agent, docker, и т. п.). С service=docker — только Docker-демон (включая openclaw).

#Параметры query

Параметр Тип Обяз. По умолч. Описание
service string нет Имя systemd-юнита: docker, app, vibe-agent
lines number нет 50 Количество строк, 1–500
since string нет Время в формате journalctl: "1 hour ago", "2026-05-11 10:00:00"
grep string нет Подстрока для фильтрации, до 200 символов

#Пример запроса — cURL

Terminal
# Последние 200 строк системного журнала с фильтром по openclaw
curl -H "X-Api-Key: vibe_live_..." \
  "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/logs?service=docker&lines=200&grep=openclaw"

#Пример запроса — JavaScript

javascript
const params = new URLSearchParams({
  service: 'docker',
  lines: '200',
  since: '30 minutes ago',
})
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/logs?${params}`,
  { headers: { 'X-Api-Key': 'vibe_live_...' } }
)
const { data } = await res.json()
data.logs.forEach((line) => console.log(line))

#Поля ответа

Поле Тип Описание
success boolean true при успехе
data.service string | null Эхо ?service=
data.logs string[] Строки журнала, новые в конце массива
data.lines string[] Алиас data.logs (backward compat)
data.requestedLines number Эхо ?lines=
data.returnedLines number Длина data.logs
data.since string | null Эхо ?since=
data.grep string | null Эхо ?grep=

#Пример успешного ответа

JSON
{
  "success": true,
  "data": {
    "service": "docker",
    "logs": [
      "May 11 09:14:21 vm-agent dockerd[812]: time=\"2026-05-11T09:14:21Z\" level=info msg=\"container start\" container=openclaw",
      "May 11 09:14:22 vm-agent dockerd[812]: time=\"2026-05-11T09:14:22Z\" level=info msg=\"openclaw: config loaded from /home/node/.openclaw/openclaw.json\""
    ],
    "lines": [
      "May 11 09:14:21 vm-agent dockerd[812]: time=\"2026-05-11T09:14:21Z\" level=info msg=\"container start\" container=openclaw",
      "May 11 09:14:22 vm-agent dockerd[812]: time=\"2026-05-11T09:14:22Z\" level=info msg=\"openclaw: config loaded from /home/node/.openclaw/openclaw.json\""
    ],
    "requestedLines": 200,
    "returnedLines": 2,
    "since": "30 minutes ago",
    "grep": null
  }
}

#Пример ответа при ошибке

404 — сервер не принадлежит этому ключу:

JSON
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Server not found"
  }
}

#Полный реинсталл

POST /v1/infra/servers/:id/deploy

Многошаговая оркестрация: stop → clean → download → runtime → install → env → pre_start → systemd → start → healthcheck. Используется как последнее средство, если контейнер openclaw не поднимается даже после docker restart или конфиг сломан так, что нужна чистая установка.

Применять с осторожностью — операция занимает 3–10 минут и блокирует другие команды на сервере (тот же лок, что и /exec).

#Поля запроса

Поле Тип Обяз. Описание
archive object да Источник архива с кодом: { url } или { content } (base64)
runtime string нет node, python, go, docker. По умолчанию определяется образом VM
install string нет Команда установки зависимостей: npm ci, pip install -r requirements.txt
start string нет Команда старта systemd-юнита
env object нет Переменные окружения для .env
healthcheck object нет { url, expectedStatus, timeoutSec } для финальной проверки

Детально структура deploy описана в Полный деплой. Maintenance-ключ работает там идентично обычному API-ключу для этого же сервера.

#Пример успешного ответа

JSON
{
  "success": true,
  "data": {
    "steps": [
      { "name": "stop", "exitCode": 0, "duration": 2 },
      { "name": "download", "exitCode": 0, "duration": 8 },
      { "name": "install", "exitCode": 0, "duration": 47 },
      { "name": "start", "exitCode": 0, "duration": 3 },
      { "name": "healthcheck", "exitCode": 0, "duration": 5 }
    ],
    "totalDuration": 65
  }
}

#Коды ошибок

HTTP Код Когда Что делать
401 KEY_EXPIRED TTL ключа (3 дня) истёк Запросить новый ключ из UI агента
401 KEY_INACTIVE Ключ отозван вручную из UI или каскадно при удалении агента Запросить новый ключ
401 MISSING_API_KEY Заголовок X-Api-Key не передан Добавить заголовок
401 INVALID_API_KEY Значение ключа не парсится / неверный hash Проверить копию строки vibe_live_… целиком
403 WRONG_KEY linkedServerId ключа не совпадает с :id в URL Использовать ключ от этого агента; нельзя ходить чужим maintenance-ключом
403 UPLOAD_PATH_DENIED Только для /upload: путь в системной директории Использовать /opt/openclaw/... или /var/lib/...
404 NOT_FOUND Сервер удалён / soft-deleted, или серверу выдан другой ключ Проверить, что агент существует и ключ создан после последнего пересоздания сервера
409 EXEC_BUSY Идёт другая /exec, /upload или /deploy Подождать или снять зависший лок через `DELETE /v1/infra/servers/:id/lock`
429 RATE_LIMIT_EXCEEDED Превышен лимит 10 операций в минуту на сервер Уменьшить частоту
503 SERVER_NOT_READY Сервер ещё не готов (SLEEPING/repair-loop), 4 эндпоинта пробудят автоматически перед операцией Подождать — операция перезапустится после первой пробуждающей попытки
500 EXEC_FAILED / UPLOAD_FAILED Внутренняя ошибка агента туннеля Прочитать /logs?service=vibe-agent
502 GATEWAY_ERROR Gateway не смог связаться с агентом Подождать 30 секунд и повторить
504 EXEC_TIMEOUT Превышен timeout команды (default 300 сек) Увеличить timeout в теле запроса или разбить на меньшие шаги

Полный список общих кодов API — Ошибки. Скоупы и состав текущего ключа — GET /v1/me (см. Метаданные текущего ключа).


#Типичные сценарии

#Сценарий 1. Бот молчит

Признак: пользователь пишет боту в B24, ответа нет, дельта messages.today в UI агента не растёт.

Terminal
SERVER=...    # ID сервера из карточки агента
KEY=vibe_live_...

# 1. Посмотреть, что вообще происходит на VM
curl -H "X-Api-Key: $KEY" \
  "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/logs?service=docker&lines=200&grep=openclaw"

# 2. Если в логе что-то типа "exit code 137" / "container died" — посмотреть последние строки контейнера
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "docker logs openclaw --tail 200", "timeout": 30}'

# 3. Если контейнер не запущен — поднять его
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "docker restart openclaw", "timeout": 60}'

# 4. Подождать 30 секунд, проверить, что отвечает
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "docker ps --format \"{{.Names}} {{.Status}}\" | grep openclaw", "timeout": 10}'

После починки: отозвать ключ из UI агента (кнопка «Отозвать ключ обслуживания») — не ждать 3 дня.

#Сценарий 2. Бот отвечает неправильно

Признак: бот реагирует, но логика поломана — не вызывает MCP-инструмент, отвечает шаблоном, использует не ту модель.

Terminal
SERVER=...
KEY=vibe_live_...

# 1. Посмотреть текущий конфиг (особенно agents.defaults.model и mcpServers)
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "cat /opt/openclaw/config/openclaw.json", "timeout": 10}'

# 2. Поправить локально, залить обратно (mode 0644 — права не ломаем)
CONTENT=$(base64 -i ./openclaw.json.fixed)
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/upload" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"content\": \"$CONTENT\",
    \"path\": \"/opt/openclaw/config/openclaw.json\",
    \"mode\": \"0644\"
  }"

# 3a. Hot-reload — без рестарта контейнера
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "docker kill -s SIGUSR1 openclaw", "timeout": 10}'

# 3b. Либо полный перезапуск
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/$SERVER/exec" \
  -H "X-Api-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "docker restart openclaw", "timeout": 60}'

После починки — снова не забыть отозвать ключ.


#Уборка после работы

Что Как
Отозвать вручную сразу после починки Кнопка «Отозвать ключ обслуживания» в карточке агента (или DELETE /api/agents/:id/maintenance-key через UI)
Авто-истечение Через 3 дня после выдачи: expiresAt истекает, requireApiKey возвращает 401 KEY_EXPIRED
Каскадное отзывание При удалении агента (DELETE /api/agents/:id) ключ автоматически переводится в status=REVOKED
Проверить статус ключа GET /api/agents/:id/maintenance-key через UI или GET /v1/me с самим ключом — увидите keyId, linkedServerId, expiresAt

Maintenance-ключ — одноразовый инструмент для конкретного инцидента. Регулярные операции делайте отдельным «обычным» менеджмент-ключом с осознанным TTL.


#Смотрите также