Облачные функции
Запускайте Python (python-v1) или Node.js (node-v1) с JSON-входом в изолированной среде gVisor. Создание и вызов доступны владельцу активной подписки на тариф с поддержкой облачных функций. Один файл UTF-8 до 256 KiB, встроенные библиотеки; установка пакетов и постоянное хранилище не поддерживаются.
От создания до результата
- Создайте функцию в разделе «Облачные функции» в панели управления. Выберите среду, введите код и настройте сеть.
- Сохраните черновик. Укажите HTTPS webhook для результата: он обязателен для публикации в любом режиме сети.
- Добавьте секреты на отдельной странице «Секреты». Значения можно записать или заменить, прочитать их из API нельзя.
- Опубликуйте функцию из сохранённого черновика. При несохранённых изменениях сначала сохраните форму.
- Вызовите и откройте историю. Введите JSON, сохраните execution ID и проверьте состояние, результат, ошибку, лог и доставку webhook.
Код, среда, сетевые разрешения и webhook входят в опубликованную версию. После их изменения сохраните и опубликуйте функцию снова. Имя, порядок доменов, пауза и live-секреты не требуют публикации. Секреты применяются к следующим исполнениям без новой публикации; принятые запуски сохраняют снимки. Пауза сохраняет место в квоте, удаление освобождает его.
Обработчик и context.http
async def handler(event, context):
response = await context.http.request(
"POST", "https://example.com/events", json=event
)
return {"status": response["status"], "execution": context.execution_id}export async function handler(event, context) {
const response = await context.http.request({
method: "POST", url: "https://example.com/events", json: event
});
return {status: response.status, execution: context.execution_id};
}event — переданный JSON. Python допускает синхронный handler; оба runtime допускают асинхронный. context содержит execution_id, deadline (UTC), secrets и http.request. Ответ HTTP: status, headers, body; body — строка UTF-8. Явный json=None или json:null отправляет JSON null, пропущенное поле json — запрос без тела. Возвращайте JSON-совместимый результат: null, число, строку, массив или объект; NaN, Infinity, bytes, undefined и циклы недопустимы.
Сеть и ошибки HTTP
Используйте только context.http для HTTPS на порту 443. Прямые socket, urllib и fetch доступа в сеть не имеют. В режиме allowlist укажите до 20 точных доменов: пустой список запрещает запросы, поддомены автоматически не разрешаются. public разрешает публичные HTTPS-имена, но не анонимный вызов функции. В обоих режимах закрыты IP-литералы, private, loopback, link-local и reserved адреса, инфраструктурные endpoint и userinfo. DNS проверяется на каждый запрос; перенаправления не выполняются. Правила webhook проверяются отдельно.
Ответы HTTP 4xx/5xx возвращаются обработчику — проверяйте status. Ошибка сети отклоняет request исключением; при необходимости обработайте её в коде. Timeout одного HTTP-запроса — 10 секунд.
Запрос к API с секретом
Замените api.example.com адресом вашего API, добавьте его домен в allowlist и сохраните API_TOKEN в секретах. Укажите свой HTTPS webhook и опубликуйте функцию. Токен не должен попадать в исходник, входной JSON или лог.
async def handler(event, context):
token = context.secrets["API_TOKEN"]
response = await context.http.request(
"POST", "https://api.example.com/events",
headers={"Authorization": f"Bearer {token}"}, json=event
)
return {"status": response["status"]}export async function handler(event, context) {
const token = context.secrets.API_TOKEN;
const response = await context.http.request({
method: "POST", url: "https://api.example.com/events",
headers: {Authorization: "Bearer " + token}, json: event
});
return {status: response.status};
}{"event":"task.completed","task_id":123}Авторизация и безопасный повтор
Используйте JWT владельца или API-ключ с отдельными scopes: functions:read для чтения, functions:write для изменений, functions:invoke для вызовов. POST-адрес не содержит токен и требует Authorization и Idempotency-Key.
/workspaces/{workspace_id}/functions/{function_id}/invokeАсинхронный запуск функции
curl --request POST 'https://api.cronbox.ru/v1/workspaces/WORKSPACE/functions/FUNCTION/invoke' \
--header 'Authorization: Bearer YOUR_SCOPED_API_KEY' \
--header 'Idempotency-Key: order-123' \
--header 'Content-Type: application/json' \
--data '{"order":123}'{"execution_id":"EXECUTION_ID","status":"queued"}202 означает принятие в очередь, а не успешное завершение. Ключ обязателен: 1–128 печатных ASCII-символов. При потере ответа повторите тот же JSON и тот же ключ: получите исходный execution_id без повторного исполнения и списания. Другой JSON с занятым ключом даёт 409. Для нового намеренного запуска нужен новый ключ. Не меняйте ключ после неопределённого сетевого исхода — можно выполнить код дважды.
/workspaces/{workspace_id}/functions/{function_id}/runsИстория состояний и доставки
/workspaces/{workspace_id}/function-runs/{execution_id}Вход, результат, ошибка и ограниченный лог
Подписанный webhook
Итоговый POST содержит event: function.completed, event_id, execution_id, function_id, version_id, status, result, error и duration_ms. Проверяйте HMAC-SHA256 по исходным байтам тела, до разбора JSON. X-Webhook-Signature имеет формат t=SECONDS,v1=HEX; X-Webhook-Timestamp содержит тот же timestamp. Формула подписи: HMAC-SHA256(secret, timestamp + «.» + raw_body). Пример отклоняет timestamp старше 300 секунд и сравнивает подписи без утечки через время сравнения.
import hashlib, hmac, time
def verify(raw_body, signature, signing_secret):
try:
parts = [part.split("=", 1) for part in signature.split(",")]
fields = dict(parts)
if len(parts) != 2 or set(fields) != {"t", "v1"}:
return False
timestamp = fields["t"]
if abs(time.time() - int(timestamp)) > 300:
return False
expected = hmac.new(signing_secret.encode(), timestamp.encode() + b"." + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(fields["v1"], expected)
except (ValueError, KeyError, TypeError):
return FalseКлюч подписи показывается однократно при создании/ротации; храните его защищённо у получателя, отдельно от runtime secrets. После ротации обновите проверку на webhook; уже принятые запуски используют свои снимки ключа. После проверки подписи атомарно сохраните event_id и выполните побочный эффект только один раз. Дубликату отвечайте 2xx.
2xx подтверждает доставку. Timeout, сеть, 408, 429 и 5xx повторяются через 30, 120, 600 секунд; другие 4xx и 3xx завершают доставку ошибкой. Тело и event_id сохраняются, timestamp подписи обновляется. Ручной повтор доставки не запускает handler заново. CronBox Heartbeat не проверяет HMAC-подпись: он подходит для тестового приёма, а проверку подписи реализуйте на своём webhook.
/workspaces/{workspace_id}/function-runs/{execution_id}/webhook/retryПовторная доставка того же результата
Квоты и ограничения
Количество функций, вызовов за календарный месяц UTC и вызовов в минуту определяется текущим тарифом владельца. Квоты общие для всех его рабочих пространств. Актуальные условия смотрите в тарифах, а свои лимиты и использование — в разделе «Облачные функции» панели управления. API capabilities возвращает текущие значения max_functions, monthly_invocations, invocations_per_minute и счётчики functions_count, accepted_count. Ноль функций запрещает создание, ноль вызовов — новые запуски; ноль не означает unlimited. Снижение квот не удаляет функции и историю, не отменяет принятые запуски и не блокирует ответ по уже принятому ключу. Если подписка закончилась или тариф больше не поддерживает функции, данные остаются читаемыми, а запись и вызовы следуют текущим разрешениям сервера.
/workspaces/{workspace_id}/functions/capabilitiesКвоты, использование и доступность исполнения
Технические ограничения платформы
Эти ограничения общие для тарифов. Их актуальные значения также возвращает API capabilities.
- CPU: 0,5 ядра; память: 512 MiB; выполнение: 60 секунд.
- 2 среды одновременно, 100 ожидающих запусков.
- Очередь: 5 минут; запуск среды: 15 секунд.
- Вход и результат: по 1 MiB; лог: до 256 KiB; глубина JSON: 128.
- HTTP: до 20 запросов, 4 одновременно, до 10 MiB трафика.
- Секреты: до 20 имён, 4 KiB на значение, 32 KiB суммарно.
- Исходный код хранится для 10 последних версий; история и идемпотентность: 7 дней.
CPU, память, время выполнения, размеры, параллельность и sandbox фиксированы платформой и не изменяются редактором тарифов. Ошибка кода расходует квоту; подтверждённый сбой платформы до исполнения возвращает резерв один раз. Неоднозначный исход после потери runner не запускает код автоматически снова.