Сначала вызови validate-only endpoint POST /v1/uploads:validate: он проверяет sizeBytes, expectedHashes.sha256, archiveManifest и buildMetadata без создания session, storage object или audit write. Затем automation автора открывает POST /v1/uploads, отправляет archive bytes напрямую в private Cloudflare R2 через signed single PUT URL или signed multipart parts, обновляет parts через POST /v1/uploads/{upload_id}/parts, проверяет GET /v1/uploads/{upload_id}, завершает POST /v1/uploads/{upload_id}/complete и отменяет POST /v1/uploads/{upload_id}/abort. Next.js API получает только metadata и lifecycle calls, а не body архива. Большая multipart session активна 48 часов, каждая signed URL - 15 минут. status.progress возвращает uploadedBytes, percent, totalParts и подтвержденные provider completedParts с ETags, поэтому Website, CLI и GitHub Action продолжают после обрыва только missing parts. Private staging object нельзя publish до complete, проверки размера/content type/archive format и security processing; bounded cleanup job abort-ит expired multipart upload или удаляет incomplete single object. Complete возвращает async job, пока scan, metadata extraction, indexing hooks или final promotion идут в фоне; опрашивай job.statusEndpoint. queued и processing являются non-terminal, а succeeded, rejected, failed и cancelled - terminal. job.progress честно показывает completion 0/1, а не выдуманный scan percentage. createdAt, updatedAt, startedAt, completedAt, retry attempts, nextAttemptAt и стабильный terminal reason позволяют отличить active, retrying и finished work без раскрытия provider prose. Канонический lifecycleStatus: draft, uploading, uploaded, validating, scanning, quarantined, ready, published, failed, cancelled; legacy status остается compatibility alias. expectedSha256 поддерживается как legacy alias, sha256_mismatch отклоняет upload до promotion. Idempotency-Key должен оставаться одинаковым для create и complete retries. Version request metadata включает version, changelog, file_category, game_slug, game_versions, loader_platforms, dependencies, requirements и build_metadata; повтор логической версии возвращает duplicate_version. metadata-only edits не требуют повторного upload bytes. Security worker атомарно claim-ит durable jobs, повторяет transient failures и переводит exhausted/unverified uploads в quarantine. ZIP policy возвращает archive_dangerous_entry, archive_encrypted_entry, archive_symlink_entry, archive_duplicate_path или archive_path_traversal; nested archives рекурсивно проверяются до глубины 3; запрещённые executable-сценарии считаются unsafe, а любой файл внутри архива выше лимита ClamAV требует manual review.
Изолированный archive-security-worker проверяет immutable scan seal: полный SHA-256, структуру ZIP/7z/RAR и ClamAV-вердикт. Exact-SHA clean cache переиспользуется только при совпадении формата, версии политики, движка и базы сигнатур. Временные сбои получают не более трёх попыток; unknown, устаревшая база и превышение ресурсов никогда не становятся clean и оставляют файл закрытым в quarantine/review.
Числовые upload defaults: single PUT до 100 MiB; multipart part 16 MiB с concurrency 4; один signing request содержит максимум 4 parts, поэтому payload in flight ограничен 64 MiB; один archive — максимум 20 GiB. Authenticated upload byte bucket — 24 GiB. Live R2 capacity probe подтвердил 64 MiB upload/download с SHA-256 equality, RSS delta ниже 384 MiB и обязательным cleanup; профиль 64 MiB × 4 был отклонён как превышающий memory budget.
Если complete создаёт non-terminal job со статусом queued или processing, HTTP-ответ имеет 202 Accepted, а заголовок Location в точности равен job.statusEndpoint. Если job уже terminal (succeeded, rejected, failed или cancelled), включая идемпотентный replay terminal результата, complete возвращает 200 OK без повторного side effect.
Повтор POST /v1/uploads с тем же body и Idempotency-Key возвращает исходную durable session с заново подписанными upload-инструкциями. Каждый single PUT URL имеет максимум 900-second lease, который сервер row-lock checkpoint-ит после sign и до response; abort-winning checkpoint отклоняет выдачу URL. Отправляй все возвращённые headers, включая If-None-Match: *. conditional 412 означает ambiguous existing write: не перезаписывай staging, а вызови тот же complete endpoint, где HEAD, immutable seal и SHA-256 являются authoritative. Signed URL и provider upload ID никогда не сохраняются в generic idempotency record; после неоднозначного create timeout сначала проверь upload-session и только потом начинай новую mutation.
Для polling guidance используйте retry.retryable, retry.strategy, retry.attempt, retry.maxAttempts, retry.nextAttemptAt и retry.retryAfterSeconds. HTTP response может передать ту же минимальную задержку через Retry-After.
Ручная форма загрузки мода на сайте использует тот же upload-session и закрытый R2 backend. Она показывает передачу файла и проверку безопасности, сохраняет созданный черновик мода после сетевого обрыва и повторно использует подтверждённые provider multipart-части при выборе того же файла. Перед multipart commit сервер повторно сверяет точный размер и provider ETag/MD5 каждой части, а асинхронный trust worker потоково проверяет SHA-256 всего объекта до ready/publish. В разделе Настройки > Для разработчиков видны последние sessions, ошибки scan и статус quarantine/review.