#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_<...>:
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
# Прочитать последние строки лога 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
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 обрезаны |
#Пример успешного ответа
{
"success": true,
"data": {
"exitCode": 0,
"stdout": "openclaw\n",
"stderr": "",
"duration": 1,
"truncated": false
}
}
#Пример ответа при ошибке
409 — на сервере уже идёт другая операция:
{
"success": false,
"error": {
"code": "EXEC_BUSY",
"message": "Another operation is running on this server"
}
}
504 — превышен таймаут:
{
"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
# Залить новый 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
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 если архив распакован |
#Пример успешного ответа
{
"success": true,
"data": {
"path": "/opt/openclaw/config/openclaw.json",
"size": 1247,
"extracted": false
}
}
#Пример ответа при ошибке
403 — путь запрещён (системные директории /etc/passwd, /root/.ssh, /boot блокируются):
{
"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
# Последние 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
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= |
#Пример успешного ответа
{
"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 — сервер не принадлежит этому ключу:
{
"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-ключу для этого же сервера.
#Пример успешного ответа
{
"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 агента не растёт.
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-инструмент, отвечает шаблоном, использует не ту модель.
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.
#Смотрите также
- Ключи и авторизация — общая модель ключей Vibe API
- Менеджмент-ключи — долгоживущие ключи для администрирования (не путать с maintenance!)
- Метаданные текущего ключа —
GET /v1/meдля проверки скоупов иlinkedServerId - Выполнить команду — полная справка по
/execдля обычных API-ключей - Загрузить файл — расширенные возможности
/upload(URL-загрузка, распаковка) - Логи сервиса — полная справка по
/logs - Полный деплой — структура
/deployи список этапов - Снять зависший лок —
DELETE /v1/infra/servers/:id/lockприEXEC_BUSYпосле обрыва - Восстановить туннель — если четыре эндпоинта стабильно возвращают
SERVER_NOT_READYпосле нескольких попыток (требует обычного API-ключа сvibe:infra, не maintenance) - Ошибки API — справочник общих кодов ошибок