Skip to content

Каталог и жизненный цикл: версия 0.2 ​

Реализовано для одного активного API, одного completion worker, PostgreSQL и local backend. Новая схема ставится миграциями релиза (npm run migrate). Дополнительные readers и барьеры обслуживания: READ_GATEWAYS. Перед обновлением остановить API/worker, сделать согласованный backup и выполнить npm run migrate, затем запустить новый код. Старые процессы не должны продолжать запись. OpenAPI: авторизованный GET /api/v1/openapi.json; полная карта текущего API.

Части и продолжение загрузки ​

Сначала POST /api/v1/repositories/{repository}/uploads с обычным descriptor и Idempotency-Key. Ответ содержит expiresAt: срок pending-сессии — 7 дней с создания по часам БД, без автоматического продления. Публикация после срока запрещена; уже опубликованные файлы не истекают.

GET uploads/{id}/parts возвращает partBytes сессии и массив {index,size,sha256}. Размер части выбирается при создании загрузки: 8 MiB, а для объектов больше 8 MiB × 10 000 — ближайшая степень двойки до 1 GiB, чтобы частей было не больше 10 000. PUT uploads/{id}/parts/{index} принимает octet-stream и обязательный X-Content-SHA256. Все части ровно partBytes, последняя может быть короче; индексы 0–9999. Части допускаются в любом порядке, но изменение одной сессии сериализуется. Повтор одинаковой части безопасен; другое содержимое того же индекса — 409. Неполная передача/неверный хеш — 422 и часть не подтверждается.

POST uploads/{id}/complete проверяет полное покрытие, собирает поток в новый blob, сверяет общий SHA-256 и только затем публикует. Повтор available-сессии возвращает тот же артефакт. До сборки части не доступны читателям. Whole-file PUT и multipart нельзя смешивать после записи частей. Пустые файлы передаются whole-file PUT. При падении после записи blob, но до commit, complete восстанавливает публикацию.

Части остаются до очистки (online cleanup репозитория, если она включена, либо offline GC): во время сборки нужен объём для частей и финального файла. Логическая квота считает descriptor один раз; физическая проверка свободного места выполняется отдельно. Для файла размера N нужен запас более 2N. Порог свободного места — 256 MiB; это не защита от внешних процессов, заполняющих диск.

Очереди ​

HTTP upload/download имеют отдельные очереди допуска: лимит активных задают ARKVORY_MAX_UPLOADS/ARKVORY_MAX_DOWNLOADS; ожидание максимум 64 запроса на направление, 8 на principal, 20 секунд. При выборе следующего запроса чередуются principal. Отмена/тайм-аут освобождают ожидание; переполнение — 503 + Retry-After. Очередь находится в памяти шлюза, после перезапуска клиент повторяет запрос. До получения слота поток upload не вычитывается приложением. Общий предел API — 128 запросов.

POST uploads/{id}/complete-async возвращает 202 с заданием. GET /api/v1/jobs/{id} доступен владельцу. Повтор enqueue возвращает существующее задание. Для исполнения запустить npm run worker; npm run worker -- --once делает одну попытку. API не исполняет очередь самостоятельно.

Задания хранятся в PostgreSQL, выбор — SKIP LOCKED, lease 30 секунд, heartbeat 10 секунд, generation проверяется при продлении/завершении. До 5 попыток, backoff 2^attempts, до 60 секунд. Ошибки целостности/прав/валидности завершают задание; временные ошибки повторяются. Максимум 10 000 активных заданий и 100 на principal. Worker перечитывает файл ключей перед исполнением: отозванные права блокируют новую попытку. Автоматического повторного запуска terminal failed-задания нет; после исправления причины можно вызвать синхронный complete.

Это очередь сборки и локальный допуск передач. Общие/per-principal bytes/s одного процесса реализованы; дополнительные readers общего хранилища используют фиксированные leased shares download-бюджета. Динамический распределённый scheduler, приоритеты и раздача с подтверждённых независимых реплик ещё не реализованы. См. TRAFFIC_CONTROL и READ_GATEWAYS.

Каталог ​

Все адреса ниже относительны /api/v1/repositories/{repository}. Читать каталог можно с read scope; изменение требует write и read доступ к артефакту. Аннотации не меняют исходный descriptor или байты.

Метод/адресДействие
GET artifacts/{id}/annotations{revision,labels,metadata,collections}; revision 0 наследует descriptor
PUT artifacts/{id}/annotations{expectedRevision,value:{labels,metadata,collections}}; полная замена, пропущенные поля становятся пустыми
POST artifacts/{id}/packageПроверка UPack ZIP и регистрация group/name/version
GET packages?group=...&name=...Курсорные страницы до 100 версий; сортировка по SemVer и группировка внутри страницы
PUT asset{path,artifactId,expectedRevision}; 0 создаёт путь, последующие ревизии меняют указатель
GET asset?path=...Текущий artifactId/revision
GET assets?prefix=...Пути, до 1000; история ревизий сохраняется в БД
GET search?q=...&label=...&collection=...&after=...Поиск по имени/значениям metadata, точной паре metadataKey/metadataValue, метке/коллекции; до 100, next для следующей страницы
GET audit?after=0Аудит изменений каталога, write scope; до 100, cursor — последний sequence
POST/DELETE artifacts/{id}/references{key}: добавить/удалить свою внешнюю ссылку

Конкурентная правка с устаревшей ревизией — 409, без потери чужих изменений. Аудит записывается в той же транзакции, что изменение. Включает аннотации, регистрацию пакета, asset pointer, восстановление ревизий и references; аудит скачиваний/всех auth-событий ещё не добавлен. Логическое удаление опубликованных файлов и retention реализованы отдельно (ARTIFACT_RETENTION): references и история привязок блокируют удаление артефакта и защищают его байты при физической очистке.

История и восстановление файлов ​

Метод/адресДействие
GET asset/history?path=...&before=...{items,next}; до 50 записей, новые первыми; before — исключающий номер ревизии
GET asset/revision?path=...&revision=...Одна неизменяемая ревизия; неизвестный путь/ревизия — 404
POST asset/restore{path,sourceRevision,expectedRevision}; создаёт новую ревизию и возвращает {path,revision,artifactId}

Запись истории: {path,revision,artifactId,actor,createdAt,sourceRevision}. Автор/дата равны null для записей до миграции 4; sourceRevision заполнен только при восстановлении. Время — UTC из БД. Чтение требует read scope; restore требует read + write. Параметры пути URL-encode, ревизии — положительные целые до 2147483647; expectedRevision для записи — до 2147483646. next=null означает конец. Новые изменения не попадают в уже начатый обход старых страниц; обновите первую страницу, чтобы увидеть их.

Восстановление не копирует байты и не удаляет более новые ревизии. Чтобы скачать выбранную ревизию, используйте возвращённый artifactId в GET artifacts/{id}/content; Range/ETag работают как обычно. Редактируемые annotations артефакта остаются текущими, история asset не является снимком аннотаций.

При 409 обновите историю и явно выберите действие заново. После потери ответа повтор с тем же expectedRevision не создаст вторую запись: он получит 409, если первая запись состоялась. Проверяйте текущий указатель и историю; не подставляйте новую expectedRevision для автоматического повтора. Аудит, новая ревизия и указатель фиксируются или откатываются вместе. Решение: ADR 0006.

SDK: assetHistory(repository,path,before?,signal?), assetRevision(repository,path,revision,signal?), restoreAsset(repository,path,sourceRevision,expectedRevision,signal?). В консоли раздел «История файла / File history» принимает путь, загружает страницы и позволяет открыть прежний artifact или восстановить его. Изменение ключа/репозитория/пути сбрасывает показанную историю.

UPack: root upack.json до 64 KiB, name/group и SemVer, сохранение пользовательских полей манифеста, неизменяемая версия. JSON должен иметь корректную UTF-8-кодировку; NUL и непарные Unicode-суррогаты в ключах/строках отклоняются без автоматической замены символов. Вложенность ограничена 128 уровнями от корня, числовые значения должны быть конечными; эти проверки выполняются до записи каталога. Идентичность пакета сравнивается без учёта регистра. ZIP читается лениво без распаковки; запрещены traversal, абсолютные пути, symlink, encryption, дубликаты. Ограничение 100 000 entries и 100 GiB суммарного заявленного uncompressed size. Проверка payload CRC и распаковка всех entries не выполняются. Архив сохраняется байт-в-байт. Это ограниченный валидатор, не обещание поддержки всех вариантов UPack/виртуальных пакетов.

Очистка и проверка диска ​

Обычная физическая очистка — фоновая online cleanup: выключена по умолчанию, настраивается на репозиторий и работает на действующем writer без остановки сервисов; логическое удаление и retention — ARTIFACT_RETENTION. Ниже описан прежний offline repair tool npm run gc и проверка диска npm run scrub.

Для offline-режима остановить API и worker и отключить их автоматический запуск на время обслуживания. Запустить npm run gc. Эксклюзивный maintenance barrier отклонит запуск при работающих новых API/worker; он не заменяет административное отключение процессов при сетевом разрыве БД.

Первый проход переводит expired pending в cancelled. После grace 24 часа следующий проход удаляет staging, parts и неопубликованный blob отменённой сессии, затем освобождает резерв в БД. Если удаление не удалось, резерв сохраняется. Повтор очистки безопасен. У available удаляются только staging/parts; опубликованный blob остаётся. Tombstone и Idempotency-Key сохраняются; фиксированного предела числа записей каталога нет, ёмкость считает только неосвобождённые байты. Файлы с неизвестным БД ID автоматически не удаляются.

npm run scrub в том же offline режиме пересчитывает SHA-256 и размер каждого available blob. При повреждении выводит ID/repository и ненулевой exit code; ничего не удаляет и не исправляет. Полный scrub 4 ТБ — отдельное длительное окно обслуживания. Scrub проверяет сохранённые bytes, а не корректность резервной копии БД.

SDK и консоль ​

@proanima/arkvory-sdk: create/status/list/search, части/resume, complete/queue/job, annotations, registerPackage, assets, cancel и потоковый download. Ответы проходят runtime validation. HTTPS обязателен, кроме loopback HTTP. Ключ получается callback, не сохраняется в localStorage. SDK не делает неограниченных скрытых retries; ошибки имеют status/code/requestId. Страницы пакетов допускают JSON до 8 MiB, остальные JSON-ответы — до 2 MiB.

/console/ после npm run build доступна с того же origin, статические файлы задаются ARKVORY_WEB_DIR (по умолчанию apps/web/public относительно cwd). Подключение, поиск, аннотации, UPack-регистрация, asset pointer, upload/resume. SHA-256 файла вычисляет Web Worker потоково; SDK удерживает до 8 MiB части. Кнопка «Пауза» прерывает текущую передачу, не отменяет сессию. Сохранить ID перед закрытием страницы. «Новая» сбрасывает поля для следующей загрузки. Скачать большой файл в браузере можно через File System Access API в Chrome/Edge; другие клиенты используют SDK/HTTP. Полный browser upload/download сценарий пока не автоматизирован.

Статическая страница публична; все данные защищены ключом. CSP запрещает сторонние scripts и frames. Разметка данных создаётся через textContent. SHA-реализация @noble/hashes поставляется со своей лицензией в /console/THIRD-PARTY.txt; права на Arkvory определяет LICENSE.md.

Для полного обхода файлового каталога используйте GET /assets/page: до 100 текущих pointers и next. Прежний GET /assets с пределом 1000 остаётся совместимым.

Поиск GET search: q — literal substring имени или значения актуальной metadata без учёта регистра; metadataKey + metadataValue — точная пара с учётом регистра, задаются вместе. Значение может быть пустой строкой. Фильтры объединяются через AND, доступ и cursor/100 сохраняются. SQL параметризован; regex/wildcards не поддерживаются. См. CLI. Элемент результата: id, name, size (десятичная строка), createdAt, publishedAt (null, если время публикации не записано), labels (текущие: из annotation, иначе из descriptor загрузки) и stages (текущие стадии продвижения в байтовом порядке, до 16). Страница и стадии читаются одним ограниченным запросом без N+1. Metadata и collections в ответ не входят: для них нужен annotation.read.

Arkvory · ProAnimaStudio