Для 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 — личный ключ
# Загрузка файла по 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-приложение
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 — личный ключ
// Загрузка архива и распаковка
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-приложение
// Встроенное 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 если архив был распакован после загрузки |
Пример ответа
{
"success": true,
"data": {
"path": "/opt/app/source.tar.gz",
"size": 1048576,
"extracted": true
}
}
Пример ответа при ошибке
403 — путь запрещён для загрузки:
{
"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задаётся только сохранённому архиву. Если нужно изменить права внутри — сделайте через/execcchmod.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 должен выдерживать пробуждение сервера плюс пятиминутный бюджет.