Для AI-агентов: markdown этой страницы — /docs-content/infra/deploy/upload.md индекс документации — /llms.txt

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

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

Записывает файл на BLACKHOLE-сервер через агент туннеля. Два варианта источника: встроенное base64-содержимое в теле запроса — до 96 МБ тела, то есть около 72 МБ самого файла, — или URL, с которого агент скачает файл сам, до 500 МБ. На серверах, включённых в rollout надёжной URL-загрузки, агент выполняет до четырёх попыток с backoff в общем бюджете пяти минут. До включения сохраняется прежнее поведение URL-загрузки. Поддерживается автоматическая распаковка архивов tar.gz / tar.bz2 / zip — формат определяется по сигнатуре файла (magic bytes) или по расширению URL.

Параметры

Параметр В Тип Обяз. Описание
id path string (UUID) да ID BLACKHOLE-сервера, status: running, blackholeStatus: CONNECTED

Поля запроса (body)

Поле Тип Обяз. Описание
path string да Путь на сервере, 1–500 символов. Для архивов — куда положить загруженный архив до распаковки
content string да (или url) Base64-содержимое файла. Максимум 96 МБ на тело запроса, то есть около 72 МБ самого файла: base64 тяжелее исходных байт примерно на треть. Тело сверх потолка отклоняется кодом 413 INLINE_SOURCE_TOO_LARGE. Файл крупнее передавайте полем url
url string да (или content) HTTPS-URL, откуда агент скачает файл. До 500 МБ
mode string нет Права файла в восьмеричном формате: 0644, 0755. Применяется к файлу по пути path
extract boolean нет Распаковать архив после загрузки. По умолчанию false. Требует content или url с архивом
extractTo string нет Директория для распаковки (при extract: true). По умолчанию — директория из path. Проверяется до начала загрузки: абсолютный путь внутри одного из корней /opt, /tmp, /root, /var/log, /var/lib, /etc/systemd, /etc/nginx, не указывающий на каталоги ключей и расписаний. Иначе — 400 INVALID_EXTRACT_TO

Нужно передать ровно одно из: content или url.

Примеры

curl — личный ключ

Terminal
# Загрузка файла по URL с автоматической распаковкой
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/upload \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://github.com/user/repo/archive/main.tar.gz",
    "path": "/opt/app/source.tar.gz",
    "extract": true,
    "extractTo": "/opt/app"
  }'

# Встроенное base64-содержимое — маленький файл (package.json)
CONTENT=$(echo '{"name":"my-app","version":"1.0.0"}' | base64)
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/upload \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"content\":\"$CONTENT\",\"path\":\"/opt/app/package.json\",\"mode\":\"0644\"}"

curl — OAuth-приложение

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/upload \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/config.json","path":"/etc/myapp/config.json"}'

JavaScript — личный ключ

javascript
// Загрузка архива и распаковка
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/upload`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      url: 'https://github.com/user/app/archive/main.tar.gz',
      path: '/opt/app/source.tar.gz',
      extract: true,
      extractTo: '/opt/app',
    }),
  }
)
const { data } = await res.json()
console.log(`Записано ${data.size} байт по пути ${data.path}${data.extracted ? ', распаковано' : ''}`)

JavaScript — OAuth-приложение

javascript
// Встроенное base64-содержимое — маленький конфиг
const content = Buffer.from('NODE_ENV=production\n').toString('base64')
await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/upload`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content,
      path: '/opt/app/.env',
      mode: '0600',
    }),
  }
)

Поля ответа

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

Пример ответа

JSON
{
  "success": true,
  "data": {
    "path": "/opt/app/source.tar.gz",
    "size": 1048576,
    "extracted": true
  }
}

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

403 — путь запрещён для загрузки:

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

Ошибки

HTTP Код Описание
400 VALIDATION_ERROR Нарушена схема: одновременно content и url, либо ни одного, неверный path
413 INLINE_SOURCE_TOO_LARGE Тело со встроенным content больше 96 МБ, то есть около 72 МБ самого файла. Решение принимается по заголовку Content-Length до чтения тела, поэтому отказ детерминированный — то же тело повторной отправкой не пройдёт. Трафик это не экономит: платформа принимает тело целиком, и отказ приходит после того, как загрузка закончилась. В error.hint приходят четыре строки — reason, recovery, recoveryAction и note: этот маршрут кладёт на машину произвольный файл, поэтому рецепт здесь — выложить файл туда, откуда машина заберёт его сама, и прислать url вместо байтов
400 NOT_BLACKHOLE Сервер в режиме OPEN — Deploy API недоступен
401 MISSING_API_KEY Не передан заголовок X-Api-Key
401 INVALID_API_KEY Неверный или просроченный API-ключ
402 ACCOUNT_FROZEN Баланс Вайбкод заморожен. Запрос отбивается до операции, спящий сервер при этом не будится — пополните баланс и повторите
403 SERVER_WAKE_BLOCKED Сервер спит, а пробуждение запрещено платформой — завершённый пробный период или административная блокировка. Загрузка такой сервер не поднимает, повтор не поможет, пока запрет не снят. Разбор запрета — Разбудить сервер
403 WRONG_KEY Сервер существует, но вызывающему не принадлежит. Операции над содержимым приложения — выкладка, команда, загрузка файла и чтение логов — открыты по любому из трёх оснований: управляющий ключ сервера, ключ, приложение которого привязано к этому серверу (Application.serverId), либо членство в команде разработки этого сервера (обе роли, «Разработчик» и «Администратор»). Токены доступа и загрузка значка требуют именно управляющего ключа. Управление самой машиной открыто ещё и роли «Администратор» команды. В ответе — hint с восстановлением из двух шагов. Подробнее — Восстановление доступа к серверу
400 INVALID_EXTRACT_TO Путь extractTo не подходит — см. описание поля в параметрах. Текст ответа называет нарушенное правило. Проверка идёт до начала загрузки
400 GALAXY_HOST_NOT_A_DEPLOY_TARGET Загрузка пришла на id машины-галактики (kind: "GALAXY"). Машина несёт контейнеры приложений и целью загрузки не является: файлы приложения кладите в архив деплоя либо создавайте их командой через /exec по id приложения. См. Galaxy-приложение
400 GALAXY_APP_USE_GALAXY_ROUTE Загрузка пришла на id приложения галактики (kind: "GALAXY_APP"). Запись шла бы на файловую систему общего сервера, а не в контейнер приложения. Файлы в такое приложение доставляются пересборкой образа из исходников — поле source в теле деплоя
403 UPLOAD_PATH_DENIED Загрузка в системные директории (/root/.ssh, /etc/passwd, /boot, …) запрещена
403 INFRA_FORBIDDEN_FOR_COWORK_KEY Вызов сделан ключом Cowork/Code — такой ключ работает только с данными, изменяющие операции ему закрыты. Что делать — Проектный ключ для деплоя
404 NOT_FOUND Сервера с таким id нет или он удалён. Сервер, который существует, но вам не принадлежит, отвечает 403 WRONG_KEY — код различает «нет такого сервера» и «нет прав на него»
409 SERVER_NOT_READY Сервер не готов к операции: не запущен или туннель не подключён. В ответе — поле hint с причиной и следующим шагом. Спящий сервер этот код не описывает — его платформа будит сама. Запустите остановленный сервер вызовом /start либо восстановите туннель вызовом /repair и повторите запрос. Случай «числится подключённым, но у Gateway нет живого туннеля» на этом маршруте приходит как 502 TUNNEL_NOT_FOUND (см. ниже)
409 WAKE_IN_PROGRESS Спящий сервер уже будит другой запрос. Дождитесь его завершения и повторите
409 EXEC_BUSY Канал команд сервера занят другой операцией — в том числе префлайтом unzip перед распаковкой zip. Это не отказ установить unzip и не повод менять формат архива. Ответ несёт retryable: true, hint и заголовок Retry-After; повторите запрос
422 VM_MISSING У записи сервера нет виртуальной машины у облачного провайдера, поэтому будить нечего. Удалите сервер и создайте новый
502 WAKE_FAILED Во время пробуждения спящего сервера он перешёл в неожиданное состояние. Повторите запрос
502 PROVIDER_ERROR Облачный провайдер вернул ошибку при запуске виртуальной машины спящего сервера. Туннель здесь ни при чём — вызов /repair не поможет
429 RATE_LIMITED Превышен лимит 10 операций в минуту на сервер
429 DEPLOY_BACKEND_BUSY Слишком много одновременных загрузок с телом запроса. Платформа ограничивает число одновременных запросов со встроенным содержимым, чтобы не исчерпать память: счётчик общий у /upload, /deploy и создания сервера с полем source.content. Ответ несёт заголовок Retry-After: 30 — повторите через 30 секунд. Либо передайте архив ссылкой (url): URL-загрузка не занимает inline-memory слот
502 UNZIP_PREFLIGHT_FAILED Не удалось установить unzip на сервере перед распаковкой zip-архива (настоящий отказ apt, не занятый канал). Чтобы не зависеть от этого шага, используйте архив .tar.gz. Занятый канал на этом шаге приходит как 409 EXEC_BUSY
500 UPLOAD_URL_DENIED / UPLOAD_DOWNLOAD_FAILED / UPLOAD_TOO_LARGE / UPLOAD_INTEGRITY_MISMATCH / UPLOAD_EXTRACT_FAILED либо другой код агента URL запрещён сетевой политикой, источник остался недоступен после исчерпания повторов, архив слишком велик, не прошёл проверку целостности или не распаковался, либо произошла другая ошибка записи. На серверах в rollout известные URL-download коды получают стабильный error.message без исходной ссылки или дословной ошибки сетевой библиотеки, прочие коды сохраняют собственное сообщение
502 TUNNEL_NOT_FOUND / GATEWAY_UNREACHABLE: … Нет живого туннеля до сервера либо Gateway недоступен — вызовите /repair и повторите. GATEWAY_UNREACHABLE несёт детали после двоеточия — сопоставляйте error.code по префиксу
503 GATEWAY_TIMEOUT: … Gateway не ответил вовремя. Код несёт детали после двоеточия — сравнивайте по префиксу. В отказоустойчивом режиме URL стабильный текст сообщает, что исход загрузки подтвердить не удалось, повтор безопасен
503 WAKE_TIMEOUT Спящий сервер не поднялся за отведённые 6.5 минуты — машина не запустилась или туннель не подключился. Сервер возвращается в sleeping, поэтому повторный запрос безопасен. См. «Известные особенности»

Полный список общих ошибок API — Ошибки.

При включённом отказоустойчивом URL-режиме проверки запроса и пробуждение VM завершаются до открытия ответа и сохраняют HTTP-коды из таблицы. Перед долгой загрузкой платформа открывает HTTP 200 и поддерживает JSON-ответ keepalive-пробелами. Её конечный исход определяется по success и error.code в теле. Поэтому ошибки агента/Gateway из уже запущенной URL-загрузки приходят под HTTP 200, а не под 500/502/503. Для content, URL до включения режима и отказов до начала долгой фазы контракт HTTP-статусов не меняется.

Известные особенности

  • Спящую отдельную виртуальную машину загрузка будит сама. Если сервер (kind: "STANDALONE") в статусе sleeping, платформа запускает пробуждение и ждёт его в том же запросе — до 6.5 минут, и только потом записывает файл. Отдельный вызов /wake не нужен, но клиентский таймаут запроса это время обязан пережить. Если сервер не поднялся, приходит 503 WAKE_TIMEOUT и сервер возвращается в sleeping — файл не записан, повторный запрос безопасен.
  • Автоопределение формата архива при extract: true. Агент определяет формат по сигнатуре файла (magic bytes, функция detectArchiveExt()): поддерживает .tar.gz, .zip, .tar.bz2. Формат можно не указывать явно. Для URL — агент также проверяет расширение.
  • macOS-архивы чистятся автоматически. Если архив содержит AppleDouble-сайдкары (._*) или .DS_Store, агент удаляет их после распаковки — это предотвращает ложные срабатывания сканеров вроде Tailwind v4 oxide. При создании архива на macOS советуем использовать COPYFILE_DISABLE=1 tar -czf ... чтобы сайдкары вообще не попадали в архив.
  • Windows PowerShell ZIP — работает на агенте ≥ 1.2.3. Compress-Archive в Windows пишет ZIP с литеральными \ в именах файлов. Агент с версии 1.2.3 автоматически нормализует эти пути через шаг normalize_windows_paths. Для старых версий агента используйте tar.gz (через WSL / Git Bash) либо выкладывайте готовый tar-архив и загружайте через url.
  • Список запрещённых путей. Агент блокирует загрузку в: /root/.ssh/ (защита SSH-ключей), /boot, /etc/shadow, /etc/passwd, системные сервис-файлы. Для приложения используйте /opt/<app>/ или /var/lib/<app>/.
  • mode применяется к архиву, не к распакованным файлам. При extract: true права распакованных файлов определяются содержимым архива, а mode задаётся только сохранённому архиву. Если нужно изменить права внутри — сделайте через /exec c chmod.
  • url скачивает сама клиентская VM, надёжный режим включается поэтапно. Этот маршрут намеренно не использует платформенный relay полного /deploy. На серверах в rollout агент выполняет до четырёх попыток с backoff в общем бюджете пяти минут: DNS/TCP/TLS/header/body-обрывы и временные HTTP-статусы повторяются, а постоянные URL-policy, HTTP, size, integrity и extraction-ошибки завершаются сразу. До включения сервер сохраняет прежний URL-контракт и таймаут. Путь в любом режиме зависит от egress/NAT этой VM. Для доставки исходников приложения, независимой от сети VM, используйте source.url полного деплоя. В надёжном режиме клиентский timeout должен выдерживать пробуждение сервера плюс пятиминутный бюджет.

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